Java內部類總結2

­
  在這個匿名內部類中,使用了缺省的構造器來生成Contents。下面的代碼展示的是,如果你的基類需要一個有參數的構造器,應該怎麼辦:
­
public class Parcel7 {
public Wrapping wrap(int x) {
// Base constructor call:
return new Wrapping(x) { // Pass constructor argument.
public int value() {
return super.value() * 47;
}
}; // Semicolon required
}
public static void main(String[] args) {
Parcel7 p = new Parcel7();
Wrapping w = p.wrap(10);
}
}
­
  只需簡單地傳遞合適的參數給基類的構造器即可,這裏是將x 傳進new Wrapping(x)。在匿名內部類末尾的分號,並不是用來標記此內部類結束(C++中是那樣)。實際上,它標記的是表達式的結束,只不過這個表達式正巧包含了內部類罷了。因此,這與別的地方使用的分號是一致的。
­
  如果在匿名類中定義成員變量,你同樣能夠對其執行初始化操作:
­
public class Parcel8 {
// Argument must be final to use inside
// anonymous inner class:
public Destination dest(final String dest) {
return new Destination() {
private String label = dest;
public String readLabel() { return label; }
};
}
public static void main(String[] args) {
Parcel8 p = new Parcel8();
Destination d = p.dest("Tanzania");
}
}
­
  如果你有一個匿名內部類,它要使用一個在它的外部定義的對象,編譯器會要求其參數引用是final 型的,就像dest()中的參數。如果你忘記了,會得到一個編譯期錯誤信息。如果只是簡單地給一個成員變量賦值,那麼此例中的方法就可以了。但是,如果你想做一些類似構造器的行爲,該怎麼辦呢?在匿名類中不可能有已命名的構造器(因爲它根本沒名字!),但通過實例初始化,你就能夠達到爲匿名內部類“製作” 一個構造器的效果。像這樣做:
­
abstract class Base {
public Base(int i) {
System.out.println("Base constructor, i = " + i);
}
public abstract void f();
}
­
public class AnonymousConstructor {
public static Base getBase(int i) {
return new Base(i) {
{
System.out.println("Inside instance initializer");
}
public void f() {
System.out.println("In anonymous f()");
}
};
}
public static void main(String[] args) {
Base base = getBase(47);
base.f();
}
}
­
  在此例中,不要求變量i 一定是final 的。因爲i 被傳遞給匿名類的基類的構造器,它並不會在匿名類內部被直接使用。下例是帶實例初始化的“parcel”形式。注意dest()的參數必須是final,因爲它們是在匿名類內被使用的。
­
public class Parcel9 {
public Destinationdest(final String dest, final float price) {
return new Destination() {
private int cost;
// Instance initialization for each object:
{
cost = Math.round(price);
if(cost > 100)
System.out.println("Over budget!");
}
­
private String label = dest;
public String readLabel() { return label; }
};
}
public static void main(String[] args) {
Parcel9 p = new Parcel9();
Destination d = p.dest("Tanzania", 101.395F);
}
}
­
  在實例初始化的部分,你可以看到有一段代碼,那原本是不能作爲成員變量初始化的一部分而執行的(就是if 語句)。所以對於匿名類而言,實例初始化的實際效果就是構造器。當然它受到了限制:你不能重載實例初始化,所以你只能有一個構造器。
­
  從多層嵌套類中訪問外部
­
  一個內部類被嵌套多少層並不重要,它能透明地訪問所有它所嵌入的外圍類的所有成員,如下所示:
­
class MNA {
private void f() {}
class A {
private void g() {}
public class B {
void h() {
g();
f();
}
}
}
}
public class MultiNestingAccess {
public static void main(String[] args) {
MNA mna = new MNA();
MNA.A mnaa = mna.new A();
MNA.A.B mnaab = mnaa.new B();
mnaab.h();
}
}
­
  可以看到在MNA.A.B中,調用方法g()和f()不需要任何條件(即使它們被定義爲private)。這個例子同時展示瞭如何從不同的類裏面創建多層嵌套的內部類對象的基本語法。“.new”語法能產生正確的作用域,所以你不必在調用構造器時限定類名。
­
  內部類的重載問題
­
  如果你創建了一個內部類,然後繼承其外圍類並重新定義此內部類時,會發生什麼呢?也就是說,內部類可以被重載嗎?這看起來似乎是個很有用的點子,但是“重載”內部類就好像它是外圍類的一個方法,其實並不起什麼作用:
­
class Egg {
private Yolk y;
­
protectedclass Yolk {
public Yolk() {
System.out.println("Egg.Yolk()");
}
}
­
public Egg() {
System.out.println("New Egg()");
y = new Yolk();
}
}
­
publicclass BigEgg extends Egg {
publicclass Yolk {
public Yolk() {
System.out.println("BigEgg.Yolk()");
}
}
­
publicstaticvoid main(String[] args) {
new BigEgg();
}
}
­
­
  輸出結果爲:
­
New Egg()
Egg.Yolk()
­
  缺省的構造器是編譯器自動生成的,這裏是調用基類的缺省構造器。你可能認爲既然創建了BigEgg 的對象,那麼所使用的應該是被“重載”過的Yolk,但你可以從輸出中看到實際情況並不是這樣的。
­
  這個例子說明,當你繼承了某個外圍類的時候,內部類並沒有發生什麼特別神奇的變化。這兩個內部類是完全獨立的兩個實體,各自在自己的命名空間內。當然,明確地繼承某個內部類也是可以的:
­
class Egg2 {
protected class Yolk {
public Yolk() {
System.out.println("Egg2.Yolk()");
}
­
public void f() {
System.out.println("Egg2.Yolk.f()");
}
}
­
private Yolk y = new Yolk();
­
public Egg2() {
System.out.println("New Egg2()");
}
­
public void insertYolk(Yolk yy) {
y = yy;
}
­
public void g() {
y.f();
}
}
­
public class BigEgg2 extends Egg2 {
public class Yolk extends Egg2.Yolk {
public Yolk() {
System.out.println("BigEgg2.Yolk()");
}
­
public void f() {
System.out.println("BigEgg2.Yolk.f()");
}
}
­
public BigEgg2() {
insertYolk(new Yolk());
}
­
public static void main(String[] args) {
Egg2 e2 = new BigEgg2();
e2.g();
}
}
­
  輸出結果爲:
­
Egg2.Yolk()
New Egg2()
Egg2.Yolk()
BigEgg2.Yolk()
BigEgg2.Yolk.f()
­
  現在BigEgg2.Yolk 通過extends Egg2.Yolk 明確地繼承了此內部類,並且重載了其中的方法。Egg2 的insertYolk()方法使得BigEgg2 將它自己的Yolk 對象向上轉型,然後傳遞給引用y。所以當g()調用y.f()時,重載後的新版的f()被執行。第二次調用Egg2.Yolk()是 BigEgg2.Yolk 的構造器調用了其基類的構造器。可以看到在調用g()的時候,新版的f()被調用了。
­
  內部類的繼承問題(thinking in java 3th p294)
­
  因爲內部類的構造器要用到其外圍類對象的引用,所以在你繼承一個內部類的時候,事情變得有點複雜。問題在於,那個“祕密的”外圍類對象的引用必須被初始化,而在被繼承的類中並不存在要聯接的缺省對象。要解決這個問題,需使用專門的語法來明確說清它們之間的關聯:
­
class WithInner {
class Inner {
Inner(){
System.out.println("this is a constructor in WithInner.Inner");
};
}
}
­
public class InheritInner extends WithInner.Inner {
// ! InheritInner() {} // Won't compile
InheritInner(WithInner wi) {
wi.super();
System.out.println("this is a constructor in InheritInner");
}
­
public static void main(String[] args) {
WithInner wi = new WithInner();
InheritInner ii = new InheritInner(wi);
}
}
­
  輸出結果爲:
­
this is a constructor in WithInner.Inner
this is a constructor in InheritInner
­
  可以看到,InheritInner 只繼承自內部類,而不是外圍類。但是當要生成一個構造器時,缺省的構造器並不算好,而且你不能只是傳遞一個指向外圍類對象的引用。此外,你必須在構造器內使用如下語法:
­
enclosingClassReference.super();
­
  這樣才提供了必要的引用,然後程序才能編譯通過
 
發佈了9 篇原創文章 · 獲贊 1 · 訪問量 4萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章