下面的程序?qū)嶋H上不會(huì)做任何事情。更糟的是,它連編譯也通不過。為什么呢?又怎么來訂正它呢?
public class Outer {
class Inner1 extends Outer{}
class Inner2 extends Inner1{}
}
這個(gè)程序看上去簡單得不可能有錯(cuò)誤,但是如果你嘗試編譯它,就會(huì)得到下面這個(gè)有用的錯(cuò)誤消息:
Outer.java:3: cannot reference this before supertype constructor has been called
class Inner2 extends Inner1{}
好吧,可能這個(gè)消息不那么有用,但是我們還是從此入手。問題在于編譯器產(chǎn)生的缺省的Inner2的構(gòu)造器為它的super調(diào)用找不到合適的外部類實(shí)例。讓我們來看看顯式地包含了構(gòu)造器的該程序:
public class Outer {
public Outer() {}
class Inner1 extends Outer{
public Inner1() {
super(); // 調(diào)用Object()構(gòu)造器
}
}
class Inner2 extends Inner1{
public Inner2() {
super(); // 調(diào)用Inner1()構(gòu)造器
}
}
}
現(xiàn)在錯(cuò)誤消息就會(huì)顯示出多一點(diǎn)的信息了:
Outer.java:12: cannot reference this before
supertype constructor has been called
super(); // 調(diào)用Inner1()構(gòu)造器
^
因?yàn)镮nner2的超類本身也是一個(gè)內(nèi)部類,一個(gè)晦澀的語言規(guī)則登場了。正如大家知道的,要想實(shí)例化一個(gè)內(nèi)部類,如類Inner1,需要提供一個(gè)外部類的實(shí)例給構(gòu)造器。一般情況下,它是隱式地傳遞給構(gòu)造器的,但是它也可以以expression.super(args)的方式通過超類構(gòu)造器調(diào)用(superclass constructor invovation)顯式地傳遞[JLS 8.8.7]。如果外部類實(shí)例是隱式傳遞的,編譯器會(huì)自動(dòng)產(chǎn)生表達(dá)式:它使用this來指代最內(nèi)部的其超類是一個(gè)成員變量的外部類。這確實(shí)有點(diǎn)繞口,但是這就是編譯器所作的事情。在本例中,那個(gè)超類就是Inner1。因?yàn)楫?dāng)前類Inner2間接擴(kuò)展了Outer類,Inner1便是它的一個(gè)繼承而來的成員。因此,超類構(gòu)造器的限定表達(dá)式直接就是this。編譯器提供外部類實(shí)例,將super重寫成this.super。 解釋到這里,編譯錯(cuò)誤所含的意思可擴(kuò)展為:
Outer.java:12: cannot reference this before
supertype constructor has been called
this.super();
^
現(xiàn)在問題就清楚了:缺省的Inner2的構(gòu)造器試圖在超類構(gòu)造器被調(diào)用前訪問this,這是一個(gè)非法的操作[JLS 8.8.7.1]。解決這個(gè)問題的蠻力方法是顯式地傳遞合理的外部類實(shí)例:
public class Outer {
class Inner1 extends Outer {}
class Inner2 extends Inner1{
public Inner2() {
Outer.this.super();
}
}
}
這樣可以通過編譯,但是它太復(fù)雜了。這里有一個(gè)更好的解決方案:無論何時(shí)你寫了一個(gè)成員類,都要問問你自己,是否這個(gè)成員類真的需要使用它的外部類實(shí)例?如果答案是否定的,那么應(yīng)該把它設(shè)為靜態(tài)成員類。內(nèi)部類有時(shí)是非常有用的,但是它們很容易增加程序的復(fù)雜性,從而使程序難以被理解。它們和泛型(謎題89)、反射(謎題80)以及繼承(本謎題)都有著復(fù)雜的交互方式。在本例中,如果你將Inner1設(shè)為靜態(tài)的便可以解決問題了。如果你將Inner2也設(shè)為靜態(tài)的,你就會(huì)真正明白這個(gè)程序做了什么:確實(shí)是一個(gè)相當(dāng)好的意外收獲。
public class Outer {
class Inner1 extends Outer{}
class Inner2 extends Inner1{}
}
這個(gè)程序看上去簡單得不可能有錯(cuò)誤,但是如果你嘗試編譯它,就會(huì)得到下面這個(gè)有用的錯(cuò)誤消息:
Outer.java:3: cannot reference this before supertype constructor has been called
class Inner2 extends Inner1{}
好吧,可能這個(gè)消息不那么有用,但是我們還是從此入手。問題在于編譯器產(chǎn)生的缺省的Inner2的構(gòu)造器為它的super調(diào)用找不到合適的外部類實(shí)例。讓我們來看看顯式地包含了構(gòu)造器的該程序:
public class Outer {
public Outer() {}
class Inner1 extends Outer{
public Inner1() {
super(); // 調(diào)用Object()構(gòu)造器
}
}
class Inner2 extends Inner1{
public Inner2() {
super(); // 調(diào)用Inner1()構(gòu)造器
}
}
}
現(xiàn)在錯(cuò)誤消息就會(huì)顯示出多一點(diǎn)的信息了:
Outer.java:12: cannot reference this before
supertype constructor has been called
super(); // 調(diào)用Inner1()構(gòu)造器
^
因?yàn)镮nner2的超類本身也是一個(gè)內(nèi)部類,一個(gè)晦澀的語言規(guī)則登場了。正如大家知道的,要想實(shí)例化一個(gè)內(nèi)部類,如類Inner1,需要提供一個(gè)外部類的實(shí)例給構(gòu)造器。一般情況下,它是隱式地傳遞給構(gòu)造器的,但是它也可以以expression.super(args)的方式通過超類構(gòu)造器調(diào)用(superclass constructor invovation)顯式地傳遞[JLS 8.8.7]。如果外部類實(shí)例是隱式傳遞的,編譯器會(huì)自動(dòng)產(chǎn)生表達(dá)式:它使用this來指代最內(nèi)部的其超類是一個(gè)成員變量的外部類。這確實(shí)有點(diǎn)繞口,但是這就是編譯器所作的事情。在本例中,那個(gè)超類就是Inner1。因?yàn)楫?dāng)前類Inner2間接擴(kuò)展了Outer類,Inner1便是它的一個(gè)繼承而來的成員。因此,超類構(gòu)造器的限定表達(dá)式直接就是this。編譯器提供外部類實(shí)例,將super重寫成this.super。 解釋到這里,編譯錯(cuò)誤所含的意思可擴(kuò)展為:
Outer.java:12: cannot reference this before
supertype constructor has been called
this.super();
^
現(xiàn)在問題就清楚了:缺省的Inner2的構(gòu)造器試圖在超類構(gòu)造器被調(diào)用前訪問this,這是一個(gè)非法的操作[JLS 8.8.7.1]。解決這個(gè)問題的蠻力方法是顯式地傳遞合理的外部類實(shí)例:
public class Outer {
class Inner1 extends Outer {}
class Inner2 extends Inner1{
public Inner2() {
Outer.this.super();
}
}
}
這樣可以通過編譯,但是它太復(fù)雜了。這里有一個(gè)更好的解決方案:無論何時(shí)你寫了一個(gè)成員類,都要問問你自己,是否這個(gè)成員類真的需要使用它的外部類實(shí)例?如果答案是否定的,那么應(yīng)該把它設(shè)為靜態(tài)成員類。內(nèi)部類有時(shí)是非常有用的,但是它們很容易增加程序的復(fù)雜性,從而使程序難以被理解。它們和泛型(謎題89)、反射(謎題80)以及繼承(本謎題)都有著復(fù)雜的交互方式。在本例中,如果你將Inner1設(shè)為靜態(tài)的便可以解決問題了。如果你將Inner2也設(shè)為靜態(tài)的,你就會(huì)真正明白這個(gè)程序做了什么:確實(shí)是一個(gè)相當(dāng)好的意外收獲。