第十章 內部類 內部類和嵌套類

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.內部類的複雜使用

  • 內部類語法 覆蓋了大量其他的更加難以理解的技術。
  1. 創建一個類,但又不希望這個類是公共可用的。像3一樣,private的內部類。
  2. 一個定義在方法中的類。
  3. 一個定義在作用域的類,此作用域在方法的內部。
  4. 一個實現了接口的匿名類。
  5. 一個匿名類,它擴展了有非默認構造器的類。
  6. 一個匿名類,他執行字段初始化。
  7. 一個匿名類,他通過實例化實現構造器(匿名類不可能有構造器)。

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();
    	}
    }
    


發佈了26 篇原創文章 · 獲贊 14 · 訪問量 8萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章