內部類的定義java
將一個類Inner的定義放在另外一個類Outer的定義內部. 則Inner的具體類型爲Outer.Inner閉包
若是要引用Inner類型, 咱們須要Outer.Inner, 即在類層次上, Inner是寄生於Outer的, 任何關於Inner的操做(如構造, 調用其方法)都須要經過Outer的實例對象生成一個Inner的對象(這樣才能跟Outer.Inner類型關聯起來)來進行操做.ide
public class Outer { public String s; Outer(String s) { this.s = s; } class Inner { public void show() { System.out.println(s); System.out.println("Inner show"); } } public Inner inner() { return new Inner(); } public static void main(String[] args) { Outer o = new Outer("???"); Outer.Inner i = o.inner(); i.show(); System.out.println(o.s); // ERROR // Outer.Inner i1 = new Outer.Inner(); Outer.Inner i1 = o.new Inner(); i1.show(); } }
就像是任何類方法內部可使用this來調用此類的全部成員同樣. 內部類也一樣使用隱式的"this指針"來訪問外部類的全部成員,而不須要任何特殊條件(相似動態語言如Python,JavaScript的閉包原理).測試
.this與.new優化
若是要生成對外部類的引用, 須要.thisthis
public class DotThis { void f() { System.out.println("DotThis.f()"); } public class Inner { public DotThis outer() { return DotThis.this; } } public Inner inner() { return new Inner(); } public static void main(String[] args) { DotThis dt = new DotThis(); DotThis.Inner dti = dt.inner(); dti.outer().f(); } }
這裏不能使用return DotThis. 是由於DotThis是一個類, 而DotThis.this是一個引用對象,指的是當前內部類所引用的外部對象.spa
若是要建立內部類的對象, 則須要.new. 在擁有外部類對象以前不可能建立內部類對象的,這是由於內部類對象會暗暗的鏈接到建立它的外部類對象上.設計
public class DotNew { public class Inner { public void show() { System.out.println("Inner show"); } } public static void main(String[] args) { DotNew dn = new DotNew(); DotNew.Inner dni = dn.new Inner(); dni.show(); } }
內部類與向上轉型指針
內部類的一個用途在於: 實現一個接口. 這樣內部類可向上轉型爲一個接口的對象:code
interface A { String toString(); } public class C { private class B implements A { public String toString() { return getClass().getName(); } } public static void main(String[] args) { C c = new C(); C.B b = c.new B(); System.out.println(b); } }
這種設計符合組合思想,接口的實現類成爲具體類的內部對象,從而很好的隱藏其實現細節.
定義在方法中的類/定義在做用域中的類
定義在方法或做用域中的類, 主要爲了解決如下狀況: 方法/做用域的邏輯過於複雜, 咱們須要建立一個類來輔助解決, 但又不但願這個類是公共可用的.
如下筆記主要基於下例幾點:
1. 一個定義在方法中的類.
2. 一個定義在做用域內的類,此做用域在方法的內部.
3. 一個實現了接口的匿名類.
4. 一個匿名類,它擴展了有非默認構造器的類.
5. 一個匿名類,它執行字段初始化.
6. 一個匿名類,它經過實例初始化實現構造.
定義在方法中的類
interface A { String toString(); } public class C { public A show() { class B implements A { public String toString() { return getClass().getName(); } } return new B(); } public static void main(String[] args) { C c = new C(); // C$1B System.out.println(c.show()); } }
定義在做用域內中的內部類
interface A { String toString(); } public class C { public String show(boolean b) { if (b) { class B implements A { public String toString() { return getClass().getName(); } } A a = new B(); return a.toString(); } else { return "error"; } } public static void main(String[] args) { C c = new C(); System.out.println(c.show(true)); System.out.println(c.show(false)); } }
匿名內部類
interface Contents { int value(); } public class Parcel7 { public Contents contents() { return new Contents() { private int i = 11; public int value() { return i; } }; } public static void main(String[] args) { Parcel7 p = new Parcel7(); Contents c = p.contents(); System.out.println(c.value()); } }
對於匿名內部類的語法解析以下: new表明新建一個對象, 調用的是Contents()構造器, 其後增長的是類的實際定義.
備註: 這跟動態語言, 如Python/JavaScript的閉包同樣.
上述的匿名內部類語法就是下述形式的簡化形式:
interface Contents { int value(); } public class Parcel7 { class MyCOntents implements Contents { private int i = 11; @Override public int value() { return i; } } public Contents contents() { return new MyCOntents(); } public static void main(String[] args) { Parcel7 p = new Parcel7(); Contents c = p.contents(); System.out.println(c.value()); } }
若是匿名內部類使用了外部的參數, 那麼其參數必須命名爲final禁止被修改. 而在匿名類中一樣可使用實例初始化(即下例代碼中大括號{}部分)來達到相似構造器的效果:
interface Contents { int value(); } public class Parcel7 { public Contents contents(final int value) { return new Contents() { private int i; { i = value; } public int value() { return i; } }; } public static void main(String[] args) { Parcel7 p = new Parcel7(); Contents c = p.contents(42); System.out.println(c.value()); } }
使用匿名內部類能夠優化其工廠方法:
interface Service { void method1(); void method2(); } interface ServiceFactory { Service getService(); } class Implementation1 implements Service { private Implementation1() {} public void method1() { System.out.println("Implementation1 method1."); } public void method2() { System.out.println("Implementation1 method2."); } public static ServiceFactory factory = new ServiceFactory() { @Override public Service getService() { return new Implementation1(); } }; } class Implementation2 implements Service { private Implementation2() {} public void method1() { System.out.println("Implementation2 method1."); } public void method2() { System.out.println("Implementation2 method2."); } public static ServiceFactory factory = new ServiceFactory() { @Override public Service getService() { return new Implementation2(); } }; } public class Factories { public static void serviceConsume(ServiceFactory fact) { Service s = fact.getService(); s.method1(); s.method2(); } public static void main(String[] args) { serviceConsume(Implementation1.factory); serviceConsume(Implementation2.factory); } }
嵌套類
使用static聲明的內部類爲嵌套類, 及它跟外圍類的實例對象並無任何的關聯.
當內部類爲static時,意味着:
1. 要建立嵌套類的對象, 並不須要其外圍類的對象.
2. 不能從嵌套類的對象中訪問非靜態的外圍類對象.
interface A { String toString(); } public class C { public static class B implements A { public String toString() { return getClass().getName(); } } public static void main(String[] args) { A a = new C.B(); System.out.println(a); } }
假設咱們須要在接口中編寫通用的公用代碼, 用於不一樣接口實現的類所公用, 那麼在接口中內嵌類是很是好的方法:
interface A{ void howdy(); class Test implements A{ public void howdy() { System.out.println("Howdy"); } public static void main(String[] args) { new Test().howdy(); } } } public class ClassInInterface implements A{ public void howdy() {} public static void main(String[] args) { A.Test t = new A.Test(); t.howdy(); } }
咱們一般在類中編寫main來測試這個類. 若是嫌麻煩咱們可使用嵌套類來實現測試代碼:
public class TestBed { public void f() { System.out.println("f()"); } public static class Tester { public static void main(String[] args) { TestBed t = new TestBed(); t.f(); } } }
執行: java TestBed$Tester便可測試.
主要緣由在於: 每一個內部類都能獨立的繼承自一個(接口的)實現, 因此不管外圍類是否已經繼承了某個(接口的)實現, 對於內部類都沒有影響.
針對多重繼承, 接口只解決了部分問題, 內部類使之獲得完善.
考慮如下場景: 即必須在一個類中以某種方式實現兩個接口. 這時候, 咱們有兩個選擇: 要麼使用單一類(所有implements兩個接口), 要麼使用內部類:
interface A { void show(); } interface B { void func(); } class X implements A, B { public void show() { System.out.println("X show"); } public void func() { System.out.println("X func"); } } class Y implements A { public void show() { System.out.println("Y show"); } B makeB() { // 返回一個匿名類 return new B() { public void func() { System.out.println("Y func"); } }; } } public class MultiInterfaces { static void takeA(A a) { a.show(); } static void takeB(B b) { b.func(); } public static void main(String[] args) { X x = new X(); Y y = new Y(); takeA(x); takeA(y); takeB(x); takeB(y.makeB()); } }
但若是擁有抽象類或具體類,而不是接口, 則只能使用內部類才能實現多重繼承.
interface A { void show(); } abstract class B { abstract void func(); } class Y implements A { public void show() { System.out.println("Y show"); } B makeB() { // 返回一個匿名類 return new B() { public void func() { System.out.println("Y func"); } }; } } public class MultiInterfaces { static void takeA(A a) { a.show(); } static void takeB(B b) { b.func(); } public static void main(String[] args) { Y y = new Y(); takeA(y); takeB(y.makeB()); } }
1. 內部類能夠有多個實例, 每一個實例都有本身的狀態信息, 而且與其外圍類對象的信息相互獨立.
2. 在單個外圍類中, 可讓多個內部類以不一樣的方式實現同一個接口, 或繼承同一個類.
3. 建立內部類對象並不依賴於外圍類對象的建立.
4. 內部類是獨立的實體.
閉包與回調
閉包是一個可調用的對象,它記錄了一些信息,這些信息來自於建立它的做用域. 經過此定義,能夠看出內部類是面向對象的閉包, 由於它不只包含外圍類對象(建立內部類的做用域)的信息, 還自動擁有一個指向此外圍類對象的引用, 在此做用域內, 內部類有權操做全部的成員, 包括private成員.
內部類的繼承
在繼承內部類的時候, 因爲內部類關聯一個外部類的實例, 因此大概格式以下:
class WithInner { class Inner{} } public class InheritInner extends WithInner.Inner { InheritInner(WithInner wi) { wi.super(); } public static void main(String[] args) { WithInner wi = new WithInner(); InheritInner ii = new InheritInner(wi); } }
內部類的覆蓋
兩個內部類是獨立的兩個實體,各自在本身的命名空間, 它們須要具體的外部類實例進行引用.
class Egg { private Yolk y; protected class Yolk { public Yolk() { System.out.println("Egg.Yolk()"); } } public Egg() { System.out.println("New Egg()"); y = new Yolk(); } } public class BigEgg extends Egg { public class Yolk { public Yolk() { System.out.println("BigEgg Yolk()"); } } public static void main(String[] args) { //New Egg() //Egg.Yolk() new BigEgg(); } }
局部內部類
咱們能夠在一個方法體的裏面建立一個內部類,局部內部類不能有訪問說明符,由於它不是外圍類的一部分,可是它能夠訪問當前代碼塊內的常量,以及此外圍類的全部成員.
下例對局部內部類與匿名內部類的建立進行了比較:
interface Counter { int next(); } public class LocalInnerClass { private int count = 0; Counter getCounter(final String name) { class LocalCounter implements Counter { public LocalCounter() { System.out.println("LocalCounter()"); } public int next() { System.out.print(name); return count++; } } return new LocalCounter(); } Counter getCounter2(final String name) { return new Counter() { { System.out.println("Counter"); } @Override public int next() { System.out.print(name); return count++; } }; } public static void main(String[] args) { LocalInnerClass lic = new LocalInnerClass(); Counter c1 = lic.getCounter("Local inner "); Counter c2 = lic.getCounter2("Anonymous inner"); for (int i = 0; i < 5; i++) System.out.println(c1.next()); for (int i = 0; i < 5; i++) System.out.println(c2.next()); } }
輸出:
LocalCounter() Counter Local inner 0 Local inner 1 Local inner 2 Local inner 3 Local inner 4 Anonymous inner5 Anonymous inner6 Anonymous inner7 Anonymous inner8 Anonymous inner9
使用局部類而不是匿名內部類的惟一理由是: 咱們須要一個已命名的構造器,或者須要重載構造器, 而匿名內部類只能用於實例初始化.