1. 程式人生 > >第十章 內部類 內部類和巢狀類

第十章 內部類 內部類和巢狀類

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