問題簡介
今天在看《Java編程思想》的時候,看到了一個很特殊的語法,懵逼了半天——一個派生類繼承自一個內部類,想要創建這個派生類的對象,首先得創建其父類的對象,也就是這個內部類,而調用內部類的構造方法創建其對象的語法,是外部類對象.super();
問題分析
我們都知道,在Java當中,當我們創建一個類的對象時,在構造方法的第一行會默認的調用父類的構造方法,創建一個父類的對象,並用super關鍵字引用父類的對象。所以若一個類,它繼承了一個內部類,那我們創建這個類的對象前,當然也必須創建一個其父類的對象,也就是這個內部類的對象。
但是,麻煩的是,對於內部類來說,有一個規則,那就是每個內部類的對象,必定要綁定一個其外部類的對象,這就是在內部類中,能夠調用外部類方法和操作外部類屬性的原因。在我們平時創建內部類對象的時候,首先需要創建一個外部類對象,在使用 外部類對象.new 內部類() 語法來創建內部類對象,這時候內部類對象綁定的就是創建它的外部類對象。
所以,當我們有一個類,它繼承自一個內部類的時,我們要創建它的對象,需要滿足兩個條件:
- 在創建對象前,要先創建好它的父類對象,也就是它繼承的內部類對象;
- 想要創建內部類對象,你得先有這個內部類的外部類對象,以供他綁定;
於是出現了下面這種讓人懵逼的代碼:
代碼案例
// 外部類
class Outer{
// 內部類
class Inner{
// 默認構造方法
Inner(){
}
// 帶參構造方法
Inner(String string){
}
}
}
// 繼承類內部類的類
public class Test01 extends Outer.Inner{
// 方式1:創建一個外部類Outer的對象,用來提供創建內部類所需的條件
private static Outer outer2 = new Outer();
// 方式2:類的構造方法傳入一個外部類的對象,用來提供創建內部類所需的條件
public Test01(Outer outer1){
// 通過外部類對象.super()調用內部類的構造方法
outer1.super("調用Inner的帶參數構造方法");
//也可以通過outer2調用
// outer2.super("調用Inner的帶參數構造方法");
}
public static void main(String[] args) {
// 創建一個外部類的對象
Outer outer = new Outer();
// 創建內部類的派生類對象,傳入外部類的引用
Test01 test01 = new Test01(outer);
}
}
代碼解讀
在上面的代碼中我們可以看到,Test01繼承了一個內部類,於是在它的構造函數中,我們需要給他提供一個外部類Outer的對象,使它在可以滿足問題分析中所說的條件2。上面的代碼使用了兩種方式來提供外部類的對象:
- 將外部類對象作為構造方法的參數傳遞進來,也就是上面代碼中的outer1;
- 為類創建一個靜態的外部類成員,也就是上面代碼中的outer2;
提供了外部類Outer的對象后,創建Test01的對象時,需要一同創建的父類對象(也就是Inner)就有了可以綁定的外部類對象。然而存在一個問題,平常我們創建一個子類的對象時,構造方法中第一行會自動調用父類的構造方法,不許要我們寫,但是這里卻不行。因為這里的父類是一個內部類,這也就意味着編譯器並不知道你想用哪個外部類對象去創建這個內部類的對象,你需要自己指定內部類綁定的外部類對象。於是,就有了上面的代碼:使用 外部類對象.super(參數) 調用內部類的構造函數,創建內部類的對象,且這個內部類對象綁定的外部類對象就是調用構造函數的內部類對象。
上面的代碼中有兩個外部類Outer的對象,使用哪個外部類對象,創建Test01對象時,一同創建的Inner對象綁定的就是哪個outer。除此之外,還有一個問題,若構造方法中有多行代碼,那外部類.super語句一定得在第一行,這和創建普通類時super語句要在第一行類似。這也很好理解,因為父類的對象一定要在子類對象之前創建。
總結
總之最重要的就是記住,一個類繼承了內部類時,在這個類中想要調用父類的構造方法,得使用外部類對象.super(參數)語法,且必須顯示的寫出來,編譯器不會自己幫你加,否則將無法成功創建類的對象。
參考文獻
《Java編程思想》