10.內部類

1.內部類定義
將一個類的定義放在另一個類的定義內部,
內部類瞭解外圍類,並能與之通信。

2. .this 和.new
.this返回外圍類的引用。
public class DotThis {
  public class Inner {
    public DotThis outer() {
      return DotThis.this;
    }
  }

.new 創建內部類,如果是非靜態的內部類,得先創建外圍類,然後纔是內部類。
public class DotNew {
  public class Inner {}
  public static void main(String[] args) {
    DotNew dn = new DotNew();
    DotNew.Inner dni = dn.new Inner();
  }
}


3.內部類可以繼承接口,可是寫在外圍類的作用域裏,可以像成員變量一樣設置private 到public 四個訪問權限,普通內部類不能含有static數據和字段。

4.匿名內部類
這中類沒有名字,只能通過繼承和實現接口創建。
這是一個匿名類的普通創建方法。
public class Parcel7 {
  public Contents contents() {
    return new Contents() { // 這裏我們可能會誤以爲是匿名類的名字是Contents,其實不是。
      private int i = 11;  
      public int value() { return i; }
    }; // Semicolon required in this case
  }
  public static void main(String[] args) {
    Parcel7 p = new Parcel7();
    Contents c = p.contents();
  }
}
上面的語法相當於
public class Parcel7b {
  class MyContents implements Contents {
    private int i = 11;
    public int value() { return i; }
  }
  public Contents contents() { return new MyContents(); }
  public static void main(String[] args) {
    Parcel7b p = new Parcel7b();
    Contents c = p.contents();
  }
}
到此我們應當明白匿名內部類確實沒有名字,只是繼承,覆寫和實現父類,這點事理解匿名內部類的關鍵。
沒有名字,也沒有構造器,可以說匿名內部類,是已存在的類和接口在某個外圍類中的純導出類,因爲沒有名字,都是通過父類和接口來控制導出類的動作,所以寫父類中沒有的動作是多餘的。

同時 注意的是匿名內部類要用外圍類的成員,得加final
5.嵌套類

靜態內部類就是嵌套類。不能用.this
嵌套類可以寫在接口內部,甚至實現外圍接口。

5.1接口的內部類都自動爲 public static. 域都是 public static final,方法都是public。

內部類不管嵌套多少層,都能透明的訪問所有它的外圍類的所有成員,直接用。

6.爲什麼需要內部類?
每個內部類都能獨立繼承一個(類或接口)的實現,不管外圍類是否已經繼承了哪個類,對內部類都沒有影響。
如果實現的接口,以下X和Y沒有區別

interface A {}
interface B {}

class X implements A, B {}

class Y implements A {
  B makeB() {
    // Anonymous inner class:
    return new B() {};
  }
}
如果都是A,B都是抽象類或具體的類,X顯然不能實現多重繼承,Y則可以。
內部類是多重繼承的一個補充。

7.閉包和回調

這個問題得舉個書上的例子,耐心看一下還是能理解的。
閉包:閉包(closure)是一個可調用的對象,它記錄了一些信息,這些信息來自於創建它的作用域。通過這個定義,可以看出內部類是面向對象的閉包,因爲它不僅包含外圍類對象(“創建內部類的作用域”)的信息,還自動擁有一個指向此外圍類對象的引用,在此作用域內,內部類有權操作所有的成員,包括private成員。

interface Incrementable {
void increment();
}
// Very simple to just implement the interface:
class Callee1 implements Incrementable {
private int i = 0;
public void increment() {
i++;
System.out.println(i);
}
}
class MyIncrement {
void increment() {
System.out.println("Other operation");
}
static void f(MyIncrement mi) { mi.increment(); }
}
// If your class must implement increment() in
// some other way, you must use an inner class:
class Callee2 extends MyIncrement {
private int i = 0;
private void incr() {
i++;
System.out.println(i);
}
private class Closure implements Incrementable {
public void increment() { incr(); }
}
Incrementable getCallbackReference() {
return new Closure();
}
}
class Caller {
private Incrementable callbackReference;
Caller(Incrementable cbh) { callbackReference = cbh; }
void go() { callbackReference.increment(); }
}
public class Callbacks {
private static Test monitor = new Test();
public static void main(String[] args) {
Callee1 c1 = new Callee1();
Callee2 c2 = new Callee2();
MyIncrement.f(c2);
Caller caller1 = new Caller(c1);
Caller caller2 = new Caller(c2.getCallbackReference());
caller1.go();
caller1.go();
caller2.go();
caller2.go();
monitor.expect(new String[] {
"Other operation",
"1",
"2",
"1",
"2"
});
}
} ///:~
這個例子進一步揭示了外圍類實現一個接口與內部類實現此接口之間的區別。就代碼而言,Callee1是簡單的解決方式。Callee2繼承自MyIncrement,後者已經有了一個不同的increment()方法,並且與Incrementable接口期望的increment()方法完全不相關。所以如果Callee2繼承了MyIncrement,就不能爲了使用Incrementable而重載increment()方法,於是你只能使用內部類獨立地實現Incrementable。還要注意,當你創建了一個內部類時,你並沒有把什麼東西加入到外圍類的接口中,也沒有修改外圍類的接口。
注意,在Callee2中除了getCallbackReference()以外,其它成員都是private的。要想建立與外部世界的任何連接,接口Incrementable都是必需的。在這裏你可以看到,接口是如何與接口的實現完全獨立的。
內部類Closure實現了Incrementable,以提供一個返回Callee2的“鉤子(hook)”。無論誰獲得此Incrementable的引用,都只能調用increment(),除此之外沒有其它功能(不像指針那樣,允許你做很多事情)
Caller的構造器需要一個Incrementable的引用作爲參數(雖然可以在任意時刻捕獲回調引用),然後在以後的某個時刻,Caller對象可以使用此引用回調Callee類。
回調的價值在於它的靈活性;你可以在運行期動態地決定需要調用什麼方法。

這些是書上的例子,沒有這些東西也不太好說清楚。
總結:實現指針,閉包中有外圍類的引用,可以通過它瞭解外圍類。
    如果類A繼承類B,又要實現接口C,但B和C中有相同的方法,功能不一樣,這時將C作爲A的內部類,並給予返回引用。可以用兩個方法,而不會混淆。

8.內部類與控制框架
設計模式將變化的事物與保持不變的事物分開,模板方法是保持不變的事物,而可覆蓋的方法是變化的事物。

9.內部類的繼承

如class WithInner {
  class Inner {}
}

public class InheritInner extends WithInner.Inner {//本類只想繼承內部類,而不是外圍類。
  //! InheritInner() {} // Won't compile
  InheritInner(WithInner wi) {     //但是外圍類必須得初始化,傳入父類
    wi.super();                    //並加入父類.super
  }
  public static void main(String[] args) {
    WithInner wi = new WithInner();
    InheritInner ii = new InheritInner(wi);
  }
}

10.內部類覆蓋

兩個類中內部類名字雖然一樣,但是它們是完全獨立的兩個實體,各自在自己的命名空間,明確繼承是可以的。
class Egg {
  private Yolk y;
  protected class Yolk {
    public Yolk() { print("Egg.Yolk()"); }
  }
  public Egg() {
    print("New Egg()");
    y = new Yolk();
  }

public class BigEgg extends Egg {
  public class Yolk {           //覆蓋不了,如果覆蓋可以寫成 public class Yolk extends Egg.Yolk
    public Yolk() { print("BigEgg.Yolk()"); }
  }
  public static void main(String[] args) {
    new BigEgg();
  }
} /* Output:
New Egg()
Egg.Yolk()
*///:~

11.局部內部類
就是寫在外圍類的方法裏的,不是外圍類的一部分,所以不能訪問外圍類的成員變量,但是可以訪問當前代碼塊內的外圍類和常量。
使用局部和匿名內部類的區別,需要構造器和返回不止一個對象。

12.內部類的標識符
每個類都會生成class文件。
如A內有內部類B,那麼A的class文件有
A.class
A$1.class 匿名類
A$B.class 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章