內部類



內部
本文主要參照網上的一些相關文章、以及thinking in java 第三版,對java裏面的內部類進行了一個較爲詳細的總結
內部是指在一個外部的內部再定一個。內部外部的一個成,並且依附於外部而存在的。內部,可用protectedprivate(而外部只能使用public和缺省的包訪問權限)。內部類主要有以下幾類:內部、局部內部、靜內部、匿名內部
 
爲什麼需要內部類?
典型的情況是,內部類繼承自某個類或實現某個接口,內部類的代碼操作創建其的外圍類的對象。所以你可以認爲內部類提供了某種進入其外圍類的窗口。使用內部類最吸引人的原因是:
每個內部類都能獨立地繼承自一個(接口的)實現,所以無論外圍類是否已經繼承了某個(接口的)實現,對於內部類都沒有影響。如果沒有內部類提供的可以繼承多個具體的或抽象的類的能力,一些設計與編程問題就很難解決。從這個角度看,內部類使得多重繼承的解決方案變得完整。接口解決了部分問題,而內部類有效地實現了“多重繼承”。
 
A:成員內部類
外部的一個成存在,與外部的屬性、方法並列。
publicclass Outer {
       privatestaticinti = 1;
       privateintj = 10;
       privateintk = 20;
 
       publicstaticvoidouter_f1() {
       }
 
       publicvoidouter_f2() {
       }
 
       // 內部中,不能定
       // 內部中,可以訪問外部的所有成
       class Inner {
              // static int inner_i = 100;//內部中不允態變
              intj = 100; // 內部和外部量可以共存
              intinner_i = 1;
 
              void inner_f1() {
                     System.out.println(i);
                         //在內部訪問內部自己的量直接用量名
                     System.out.println(j);
                         //在內部訪問內部自己的量也可以用this.量名
                     System.out.println(this.j);
                         //在內部訪問外部中與內部同名的量用外部.this.量名
                     System.out.println(Outer.this.j);
                         //如果內部中沒有與外部同名的量,可以直接用量名訪問外部類變
                     System.out.println(k);
                                          outer_f1();
                                          outer_f2();
              }
       }
 
       //外部的非靜方法訪問內部
       publicvoidouter_f3() {
              Inner inner = new Inner();
              inner.inner_f1();
       }
 
       // 外部的靜方法訪問內部,與在外部外部訪問內部
       publicstaticvoidouter_f4() {
              //step1 建立外部類對
              Outer out = new Outer();
              //step2 根據外部類對象建立內部類對
              Inner inner = out.new Inner();
              //step3 訪問內部的方法
              inner.inner_f1();
       }
 
       publicstaticvoid main(String[] args) {
              //outer_f4();//該語句的果和下面三條句的果一
              //如果要直接建內部象,不能想當然地認爲只需加上外圍類Outer的名字,
              //就可以按照通常的子生成內部象,而是必使用此外圍類的一個象來
              //建其內部的一個象:
              //Outer.Inner outin = out.new Inner()
              //因此,除非你已有了外圍類的一個象,否不可能生成內部象。因
              //內部象會悄悄地接到建它的外圍類象。如果你用的是靜的內部
              //那就不需要其外圍類對象的引用。
              Outer out = new Outer();
              Outer.Inner outin = out.new Inner();
              outin.inner_f1();
       }
}

注意:內部是一個編譯時的概念,一旦編譯成功,就會成完全不同的兩於一個名outer的外部和其內部定的名inner的內部編譯完成後出outer.classouter$inner.class
 
B:局部內部類
在方法中定的內部局部內部。與局部似,局部內部類不能有訪問說明符,因爲它不是外圍類的一部分,但是它可以訪問當前代碼塊內的常量,和此外圍類所有的成員。
 
publicclass Outer {
       privateints = 100;
       privateintout_i = 1;
 
       publicvoid f(finalint k) {
              finalint s = 200;
              int i = 1;
              finalint j = 10;
             
                 //在方法內部
              class Inner {
                     ints = 300;// 可以定與外部同名的
 
                     // static int m = 20;//不可以定態變
                     Inner(int k) {
                            inner_f(k);
                     }
 
                     intinner_i = 100;
 
                     voidinner_f(int k) {
                         //如果內部沒有與外部同名的量,在內部中可以直接訪問外部
                            System.out.println(out_i);
                         //可以訪問外部的局部(即方法內的),但是量必final
                            System.out.println(j);
                            //System.out.println(i);
                         //如果內部中有與外部同名的量,直接用量名訪問的是內部
                            System.out.println(s);
                         //this.量名訪問的也是內部類變
                            System.out.println(this.s);
                         //用外部.this.內部類變量名訪問的是外部類變
                            System.out.println(Outer.this.s);
                     }
              }
              new Inner(k);
       }
 
       publicstaticvoid main(String[] args) {
              // 訪問局部內部先有外部類對
              Outer out = new Outer();
              out.f(3);
       }
}
 
C:靜態內部類(嵌套類)(注意:前兩內部似,所以可以照參考量)
如果你不需要內部類對象與其外圍類對象之間有聯繫,那你可以將內部類聲明爲static。這通常稱爲嵌套類(nested class)。想要理解static應用於內部類時的含義,你就必須記住,普通的內部類對象隱含地保存了一個引用,指向創建它的外圍類對象。然而,當內部類是static的時,就不是這樣了。嵌套類意味着:
1. 要創建嵌套類的對象,並不需要其外圍類的對象。
2. 不能從嵌套類的對象中訪問非靜態的外圍類對象。
 
publicclass Outer {
       privatestaticinti = 1;
       privateintj = 10;
       publicstaticvoidouter_f1() {
       }
 
       publicvoidouter_f2() {
       }
 
       // 內部可以用public,protected,private
       // 內部中可以定或者非靜的成
       staticclass Inner {
              staticintinner_i = 100;
              intinner_j = 200;
              staticvoidinner_f1() {
                      //內部只能訪問外部的靜(包括靜態變量和靜方法)
                     System.out.println("Outer.i" + i);
                     outer_f1();
              }
 
              voidinner_f2() {
                     // 內部不能訪問外部的非靜(包括非靜態變量和非靜方法)
                     // System.out.println("Outer.i"+j);
                     // outer_f2();
              }
       }
 
       publicvoidouter_f3() {
              // 外部類訪問內部的靜:內部.
              System.out.println(Inner.inner_i);
              Inner.inner_f1();
              // 外部類訪問內部的非靜:例化內部即可
              Inner inner = new Inner();
              inner.inner_f2();
       }
 
       publicstaticvoid main(String[] args) {
              newOuter().outer_f3();
       }
}

生成一個靜內部不需要外部是靜內部和成內部的區。靜內部象可以直接生成:Outer.Inner in = new Outer.Inner();而不需要通生成外部類對象來生成。這樣實際上使靜內部了一個頂級類(正常情況下,你不能在接口內部放置任何代碼,但嵌套類可以作爲接口的一部分,因爲它是static 的。只是將嵌套類置於接口的命名空間內,這並不違反接口的規則)
 
D:匿名內部類(from thinking in java 3th)
簡單地說:匿名內部類就是沒有名字的內部類。什麼情況下需要使用匿名內部類?如果滿足下面的一些條件,使用匿名內部類是比較合適的:
  ·只用到類的一個實例。 
  ·類在定義後馬上用到。 
  ·類非常小(SUN推薦是在4行代碼以下) 
  ·給類命名並不會導致你的代碼更容易被理解。
  在使用匿名內部類時,要記住以下幾個原則:
  ·匿名內部類不能有構造方法。 
  ·匿名內部類不能定義任何靜態成員、方法和類。 
  ·匿名內部類不能是public,protected,private,static 
  ·只能創建匿名內部類的一個實例。
·一個匿名內部類一定是在new的後面,用其隱含實現一個接口或實現一個類。 
  ·因匿名內部類爲局部內部類,所以局部內部類的所有限制都對其生效。 

 
下面的例子看起來有點奇怪:
//在方法中返回一個匿名內部類
public class Parcel6 {
        public Contents cont() {
                return new Contents() {
                     private int i = 11;
 
                     public int value() {
                            return i;
                     }
              }; // 在這裏需要一個分號
        }
 
        public static void main(String[] args) {
                Parcel6 p = new Parcel6();
                Contents c = p.cont();
        }
}
cont()方法將下面兩個動作合併在一起:返回值的生成,與表示這個返回值的類的定義!進一步說,這個類是匿名的,它沒有名字。更糟的是,看起來是你正要創建一個Contents對象:
return new Contents()
但是,在到達語句結束的分號之前,你卻說:“等一等,我想在這裏插入一個類的定義”:
return new Contents() {
private int i = 11;
public int value() { return i; }
};
這種奇怪的語法指的是:“創建一個繼承自Contents的匿名類的對象。”通過new 表達式返回的引用被自動向上轉型爲對Contents的引用。匿名內部類的語法是下面例子的簡略形式:
class MyContents implements Contents {
private int i = 11;
public int value() { return i; }
    }
return new MyContents();
在這個匿名內部類中,使用了缺省的構造器來生成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);
    }
}
只需簡單地傳遞合適的參數給基類的構造器即可,這裏是將傳進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();
    }
}
在此例中,不要求變量一定是final 的。因爲被傳遞給匿名類的基類的構造器,它並不會在匿名類內部被直接使用。下例是帶實例初始化的“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 明確地繼承了此內部類,並且重載了其中的方法。Egg2insertYolk()方法使得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();
這樣才提供了必要的引用,然後程序才能編譯通過。
內部
本文主要參照網上的一些相關文章、以及thinking in java 第三版,對java裏面的內部類進行了一個較爲詳細的總結
內部是指在一個外部的內部再定一個。內部外部的一個成,並且依附於外部而存在的。內部,可用protectedprivate(而外部只能使用public和缺省的包訪問權限)。內部類主要有以下幾類:內部、局部內部、靜內部、匿名內部
 
爲什麼需要內部類?
典型的情況是,內部類繼承自某個類或實現某個接口,內部類的代碼操作創建其的外圍類的對象。所以你可以認爲內部類提供了某種進入其外圍類的窗口。使用內部類最吸引人的原因是:
每個內部類都能獨立地繼承自一個(接口的)實現,所以無論外圍類是否已經繼承了某個(接口的)實現,對於內部類都沒有影響。如果沒有內部類提供的可以繼承多個具體的或抽象的類的能力,一些設計與編程問題就很難解決。從這個角度看,內部類使得多重繼承的解決方案變得完整。接口解決了部分問題,而內部類有效地實現了“多重繼承”。
 
A:成員內部類
外部的一個成存在,與外部的屬性、方法並列。
publicclass Outer {
       privatestaticinti = 1;
       privateintj = 10;
       privateintk = 20;
 
       publicstaticvoidouter_f1() {
       }
 
       publicvoidouter_f2() {
       }
 
       // 內部中,不能定
       // 內部中,可以訪問外部的所有成
       class Inner {
              // static int inner_i = 100;//內部中不允態變
              intj = 100; // 內部和外部量可以共存
              intinner_i = 1;
 
              void inner_f1() {
                     System.out.println(i);
                         //在內部訪問內部自己的量直接用量名
                     System.out.println(j);
                         //在內部訪問內部自己的量也可以用this.量名
                     System.out.println(this.j);
                         //在內部訪問外部中與內部同名的量用外部.this.量名
                     System.out.println(Outer.this.j);
                         //如果內部中沒有與外部同名的量,可以直接用量名訪問外部類變
                     System.out.println(k);
                                          outer_f1();
                                          outer_f2();
              }
       }
 
       //外部的非靜方法訪問內部
       publicvoidouter_f3() {
              Inner inner = new Inner();
              inner.inner_f1();
       }
 
       // 外部的靜方法訪問內部,與在外部外部訪問內部
       publicstaticvoidouter_f4() {
              //step1 建立外部類對
              Outer out = new Outer();
              //step2 根據外部類對象建立內部類對
              Inner inner = out.new Inner();
              //step3 訪問內部的方法
              inner.inner_f1();
       }
 
       publicstaticvoid main(String[] args) {
              //outer_f4();//該語句的果和下面三條句的果一
              //如果要直接建內部象,不能想當然地認爲只需加上外圍類Outer的名字,
              //就可以按照通常的子生成內部象,而是必使用此外圍類的一個象來
              //建其內部的一個象:
              //Outer.Inner outin = out.new Inner()
              //因此,除非你已有了外圍類的一個象,否不可能生成內部象。因
              //內部象會悄悄地接到建它的外圍類象。如果你用的是靜的內部
              //那就不需要其外圍類對象的引用。
              Outer out = new Outer();
              Outer.Inner outin = out.new Inner();
              outin.inner_f1();
       }
}

注意:內部是一個編譯時的概念,一旦編譯成功,就會成完全不同的兩於一個名outer的外部和其內部定的名inner的內部編譯完成後出outer.classouter$inner.class
 
B:局部內部類
在方法中定的內部局部內部。與局部似,局部內部類不能有訪問說明符,因爲它不是外圍類的一部分,但是它可以訪問當前代碼塊內的常量,和此外圍類所有的成員。
 
publicclass Outer {
       privateints = 100;
       privateintout_i = 1;
 
       publicvoid f(finalint k) {
              finalint s = 200;
              int i = 1;
              finalint j = 10;
             
                 //在方法內部
              class Inner {
                     ints = 300;// 可以定與外部同名的
 
                     // static int m = 20;//不可以定態變
                     Inner(int k) {
                            inner_f(k);
                     }
 
                     intinner_i = 100;
 
                     voidinner_f(int k) {
                         //如果內部沒有與外部同名的量,在內部中可以直接訪問外部
                            System.out.println(out_i);
                         //可以訪問外部的局部(即方法內的),但是量必final
                            System.out.println(j);
                            //System.out.println(i);
                         //如果內部中有與外部同名的量,直接用量名訪問的是內部
                            System.out.println(s);
                         //this.量名訪問的也是內部類變
                            System.out.println(this.s);
                         //用外部.this.內部類變量名訪問的是外部類變
                            System.out.println(Outer.this.s);
                     }
              }
              new Inner(k);
       }
 
       publicstaticvoid main(String[] args) {
              // 訪問局部內部先有外部類對
              Outer out = new Outer();
              out.f(3);
       }
}
 
C:靜態內部類(嵌套類)(注意:前兩內部似,所以可以照參考量)
如果你不需要內部類對象與其外圍類對象之間有聯繫,那你可以將內部類聲明爲static。這通常稱爲嵌套類(nested class)。想要理解static應用於內部類時的含義,你就必須記住,普通的內部類對象隱含地保存了一個引用,指向創建它的外圍類對象。然而,當內部類是static的時,就不是這樣了。嵌套類意味着:
1. 要創建嵌套類的對象,並不需要其外圍類的對象。
2. 不能從嵌套類的對象中訪問非靜態的外圍類對象。
 
publicclass Outer {
       privatestaticinti = 1;
       privateintj = 10;
       publicstaticvoidouter_f1() {
       }
 
       publicvoidouter_f2() {
       }
 
       // 內部可以用public,protected,private
       // 內部中可以定或者非靜的成
       staticclass Inner {
              staticintinner_i = 100;
              intinner_j = 200;
              staticvoidinner_f1() {
                      //內部只能訪問外部的靜(包括靜態變量和靜方法)
                     System.out.println("Outer.i" + i);
                     outer_f1();
              }
 
              voidinner_f2() {
                     // 內部不能訪問外部的非靜(包括非靜態變量和非靜方法)
                     // System.out.println("Outer.i"+j);
                     // outer_f2();
              }
       }
 
       publicvoidouter_f3() {
              // 外部類訪問內部的靜:內部.
              System.out.println(Inner.inner_i);
              Inner.inner_f1();
              // 外部類訪問內部的非靜:例化內部即可
              Inner inner = new Inner();
              inner.inner_f2();
       }
 
       publicstaticvoid main(String[] args) {
              newOuter().outer_f3();
       }
}

生成一個靜內部不需要外部是靜內部和成內部的區。靜內部象可以直接生成:Outer.Inner in = new Outer.Inner();而不需要通生成外部類對象來生成。這樣實際上使靜內部了一個頂級類(正常情況下,你不能在接口內部放置任何代碼,但嵌套類可以作爲接口的一部分,因爲它是static 的。只是將嵌套類置於接口的命名空間內,這並不違反接口的規則)
 
D:匿名內部類(from thinking in java 3th)
簡單地說:匿名內部類就是沒有名字的內部類。什麼情況下需要使用匿名內部類?如果滿足下面的一些條件,使用匿名內部類是比較合適的:
  ·只用到類的一個實例。 
  ·類在定義後馬上用到。 
  ·類非常小(SUN推薦是在4行代碼以下) 
  ·給類命名並不會導致你的代碼更容易被理解。
  在使用匿名內部類時,要記住以下幾個原則:
  ·匿名內部類不能有構造方法。 
  ·匿名內部類不能定義任何靜態成員、方法和類。 
  ·匿名內部類不能是public,protected,private,static 
  ·只能創建匿名內部類的一個實例。
·一個匿名內部類一定是在new的後面,用其隱含實現一個接口或實現一個類。 
  ·因匿名內部類爲局部內部類,所以局部內部類的所有限制都對其生效。 

 
下面的例子看起來有點奇怪:
//在方法中返回一個匿名內部類
public class Parcel6 {
        public Contents cont() {
                return new Contents() {
                     private int i = 11;
 
                     public int value() {
                            return i;
                     }
              }; // 在這裏需要一個分號
        }
 
        public static void main(String[] args) {
                Parcel6 p = new Parcel6();
                Contents c = p.cont();
        }
}
cont()方法將下面兩個動作合併在一起:返回值的生成,與表示這個返回值的類的定義!進一步說,這個類是匿名的,它沒有名字。更糟的是,看起來是你正要創建一個Contents對象:
return new Contents()
但是,在到達語句結束的分號之前,你卻說:“等一等,我想在這裏插入一個類的定義”:
return new Contents() {
private int i = 11;
public int value() { return i; }
};
這種奇怪的語法指的是:“創建一個繼承自Contents的匿名類的對象。”通過new 表達式返回的引用被自動向上轉型爲對Contents的引用。匿名內部類的語法是下面例子的簡略形式:
class MyContents implements Contents {
private int i = 11;
public int value() { return i; }
    }
return new MyContents();
在這個匿名內部類中,使用了缺省的構造器來生成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);
    }
}
只需簡單地傳遞合適的參數給基類的構造器即可,這裏是將傳進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();
    }
}
在此例中,不要求變量一定是final 的。因爲被傳遞給匿名類的基類的構造器,它並不會在匿名類內部被直接使用。下例是帶實例初始化的“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 明確地繼承了此內部類,並且重載了其中的方法。Egg2insertYolk()方法使得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();
這樣才提供了必要的引用,然後程序才能編譯通過。
發佈了17 篇原創文章 · 獲贊 9 · 訪問量 2萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章