第十章 內部類 內部類和巢狀類
阿新 • • 發佈:2018-12-26
1.內部類訪問外圍類的成員方法
- 內部類可以訪問其外圍類的方法和欄位,就像他們自己的一樣。
- 當某個外圍類的物件建立一個內部類物件時,此內部類物件必定會祕密地捕獲一個指向那個外圍類物件的引用。
在訪問此外圍類成員的時候,就用那個引用去訪問外圍類的成員,內部類物件只能在與其外圍類的物件相關聯
的時候才能被建立。編譯器會處理這些細節。
2.使用.this和.new
- .this生成一個外部類物件的引用。
public class DotThis { void f(){System.out.println("DotThis.f()");} public class Inner{ public DotThis outer(){ //.this 返回外部類物件 return DotThis.this; } } public Inner inner(){return new Inner();} public static void main(String[] args) { DotThis dt = new DotThis(); Inner dti = dt.inner(); dti.outer().f(); } }
- .new 生成內部類的物件。
可以看到上面的程式碼必須生成一個外部類物件,然後才能生成內部類物件。public class DotNew { public class Inner{} public static void main(String[] args) { DotNew dn = new DotNew(); //注意這裡 你不能new DotNew.Inner(); Inner dni = dn.new Inner(); } }
因為在擁有外部類物件之前是不可能建立內部類物件的。這是因為內部類物件會隱式的連線到建立它的外部類物件上。
3.內部類和向上轉型
- 當內部類向上轉型為其父類的時候,得到的只是指向父類或介面的引用,所以只能呼叫父類或介面中所宣告的方法
而宣告private或protected(除非它的子類或同一包下的或外部類能訪問)的內部類可以實現外部類外的類不能宣告這個內部類的具體引用,所以內部類就實現了細節的隱藏。interface Destination{ String readLabel(); } interface Contents{ int value(); } class Parcel{ //內部類 private class PContents implements Contents{ private int i = 11; public int value() {return i;} } //內部類 protected class PDestination implements Destination{ private String label; private PDestination(String whereTo){label = whereTo;} public String readLabel() {return null;} } public Destination destination(String s){return new PDestination(s);} public Contents contents(){return new PContents();} } public class TestParcel { public static void main(String[] args) { Parcel p = new Parcel(); //通過p的方法獲得,進行了向上轉型 Contents c = p.contents(); // c = p.new PContents();這種是錯誤的行為,因為PContents是Parcel的私有內部類 Destination d = p.destination("哈哈"); } }
4.內部類的複雜使用
- 內部類語法 覆蓋了大量其他的更加難以理解的技術。
- 建立一個類,但又不希望這個類是公共可用的。像3一樣,private的內部類。
- 一個定義在方法中的類。
- 一個定義在作用域的類,此作用域在方法的內部。
- 一個實現了介面的匿名類。
- 一個匿名類,它擴充套件了有非預設構造器的類。
- 一個匿名類,他執行欄位初始化。
- 一個匿名類,他通過例項化實現構造器(匿名類不可能有構造器)。
4.1.一個定義在方法(域)中的內部類
- 定義在方法中的內部類在方法外是不可以訪問這個內部類的。
interface Destination{ String readLabel(); } class Parcel{ //方法 public Destination destination(String s){ //內部類 class PDestination implements Destination{ private String label; private PDestination(String whereTo){label = whereTo;} public String readLabel() {return label;} } //返回這個物件,可以寫一個引用獲得這個物件 return new PDestination(s); } } public class TestParcel { public static void main(String[] args) { Parcel p = new Parcel(); Destination d = p.destination("呵呵"); } }
4.2.定義在作用域內的類
- 定義在作用域內的類,在作用域外是不能夠訪問這個類的。
public class Parcel6 { private void internalTracking(boolean b){ if (b) { //定義在作用域內的類,作用域外不可訪問 class TrackingSlip{ private String id; TrackingSlip(String s){ id = s; } String getSlip(){return id;} } TrackingSlip ts = new TrackingSlip("x"); String s = ts.getSlip(); } } public void track(){internalTracking(true);} public static void main(String[] args) { Parcel6 p = new Parcel6(); p.track(); } }
4.3實現了介面匿名內部類
- 匿名內部類一定伴隨著抽象類或介面的繼承。
上面是下面簡化形式:??有問題interface Contents{ int value(); } public class TestParcel { public Contents contents(){ //可以看到下面這個實現了Contents介面的類並沒有名字,它看起來像是建立了一個Contents物件。 return new Contents() { private int i = 11; public int value() {return i;} }; } public static void main(String[] args) { TestParcel p = new TestParcel(); Contents c = p.contents(); } }
interface Contents{ int value(); } public class TestParcel { 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) { TestParcel p = new TestParcel(); Contents c = p.contents(); } }
4.4擴充套件了非預設構造器的匿名內部類
class Wrapping{//這是一個普通的類 private int i; public Wrapping(int x){i = x;} public int value(){return i;} } public class Parcel7 { public Wrapping wrapping(int x){ //也可以這樣 return new Wrapping(x){ public int value() { return super.value()*47; } };//此分號和其他return表示式的分號一樣,代表表示式結束。 } }
4.5執行欄位初始化的內部類
- 如果一個匿名內部類希望使用一個在其外定義的物件,那麼編譯器會要求其引數引用是final的。
interface Destination{ String readLabel(); } public class TestParcel { //final的引數 public Destination destination(final String dest){ return new Destination() { //初始化 private String label = dest; public String readLabel() {return label;} }; } public static void main(String[] args) { TestParcel p = new TestParcel(); Destination d = p.destination("哈哈"); } }
4.6例項化實現構造的匿名內部類
- 匿名內部類中不可能有命名構造器(因為它們根本就沒有名字),但可以通過實力初始化,就能夠達到為匿名內部類建立一個構造器的效果,但並不是一個構造器,只是達到效果。
abstract class Base{ public Base(int i){ System.out.println("Base constructor, i = " + i); } public abstract void f(); } public class AnonymousConstructor { //引數i不必為final ,因為沒有在匿名內部類中使用 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(); } }
- 例項初始化的實際效果就是構造器。但它受到了限——你不能過載例項初始化方法,所以你僅有一個這樣的構造器。
4.7匿名內部類的限制
- 匿名內部類既可以擴充套件類,也可以實現介面,但不能兩者兼備。而且如果是實現介面,也只能實現一個。
- 匿名內部類可以實現工廠模式。
5.巢狀類
- 如果不需要內部類物件與外部類物件之間有聯絡,就將內部類宣告為static。通常被稱為巢狀類。
- 普通的內部類物件是隱式的儲存了一個指向外圍類物件的引用。當內部類是static意味著
要建立巢狀類的物件,並不需要依賴其外圍類的物件
不能從巢狀類的物件中訪問非靜態的外圍類物件。 - 普通內部類的欄位和方法,只能放在類的外部層次上,所以普通的內部類不能有static資料和static欄位,也不能包含巢狀類。但巢狀類可以。
interface Destination{ String readLabel(); } interface Contents{ int value(); } public class TestParcel { //巢狀類 private static class ParcelContents implements Contents{ private int i = 11; public int value() {return i;} } //巢狀類 protected static class ParcelDestination implements Destination{ private String label; //private的構造方法 這表示外面是無法初始化的 private ParcelDestination(String whereTo){ label = whereTo; } public String readLabel() {return label;} //定義一些靜態方法 public static void f(){} static int x = 10; //巢狀類在內部再次巢狀一個類 static class AnotherLevel{ public static void f(){} static int x = 10; } } public static Destination destination(String s){ return new ParcelDestination(s); } public static Contents contents(){ return new ParcelContents(); } public static void main(String[] args) { //可以看到,這裡獲取巢狀的內部類並不需要外部類的支援了,內部類不需要與外部類關聯才能建立了。 Contents c = contents(); Destination d = destination("呵呵"); } }
6.介面內部的類
- 放到介面中的任何類都是public和static的。因為是static的,只是將巢狀類置於介面的名稱空間內。
public interface ClassInInterface { void howdy(); //內部類中實現外圍介面。 class Test implements ClassInInterface{ public void howdy() { System.out.println("Howdy!"); } public static void main(String[] args) { new Test().howdy(); } } }
- 如果你想要建立某些公共方法,使得他們能夠被某個介面的所有不同實現公用,那麼使用介面巢狀類會更方便。
7.從多層巢狀類中訪問外部類的成員
- 一個內部類被巢狀多少層並不重要——它能夠透明的訪問所有它嵌入的外圍類的所有成員。
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(); } }