《java編程思想》學習筆記——內部類五

10.5 在方法和做用域內的內部類java

一、能夠在一個方法裏面或者任意的做用域內定義內部類。這麼作有兩個理由:設計模式

(1)實現某類型的接口,因而能夠建立並返回對其的引用。閉包

(2)要解決一個複雜的問題,想建立一個類來輔助你的解決方案,可是又不但願這個類是共工可用的。 如下是局部內部類:app

public class Parcel5
{
    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 static void mian(String[] args)
    {
        Parcel5 p = new Parcel5();
        Destination d = p.destination("");
    }
}

二、如下的內部類TrackingSlip其實和其餘的類一塊兒編譯過了。然而在定義TrackingSlip的做用域以外,它是不可用的,除此以外,它和普通的類同樣。框架

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("slip");
      String s = ts.getSlip();
    }
    // Can't use it here! Out of scope:
    //! TrackingSlip ts = new TrackingSlip("x");
  }	
  public void track() { internalTracking(true); }
  public static void main(String[] args) {
    Parcel6 p = new Parcel6();
    p.track();
  }
}

10.6匿名內部類ide

一、使用默認構造器this

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

以上寫法等價於如下寫法:設計

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();
    System.out.println(c.value());
  }
}

二、基類須要一個有參數的構造器code

public class Wrapping {
  private int i;
  public Wrapping(int x) { i = x; }
  public int value() { return i; }
} 
public class Parcel8 {
  public Wrapping wrapping(int x) {
    return new Wrapping(x) { 
      public int value() {
        return super.value() * 47;
      }
    }; 
  }
  public static void main(String[] args) {
    Parcel8 p = new Parcel8();
    Wrapping w = p.wrapping(10);
  }
}

三、若是定義一個匿名內部類,而且但願它使用一個在其外部定義的對象,那麼編譯器會要求其參數時final的。如:對象

public class Parcel9 {
  public Destination destination(final String dest) {
    return new Destination() {
      private String label = dest;
      public String readLabel() { return label; }
    };
  }
  public static void main(String[] args) {
    Parcel9 p = new Parcel9();
    Destination d = p.destination("Tasmania");
  }
}

四、在匿名內部類中不可能有命名構造器(由於它根本沒有名字),但經過實例初始化,就能達到爲匿名內部類建立一個構造器的效果。如:

abstract class Base {
  public Base(int i) {
    System.out.print("Base constructor, i = " + i);
  }
  public abstract void f();
}	

public class AnonymousConstructor {
  public static Base getBase(int i) {
    return new Base(i) {
      {  System.out.print("Inside instance initializer"); }
      public void f() {
    	  System.out.print("In anonymous f()");
      }
    };
  }
  public static void main(String[] args) {
    Base base = getBase(47);
    base.f();
  }
}

此例中,不要求變量i必定是final的。由於i被傳遞給匿名類的基類的 構造器,它並不會在匿名內部被直接使用。

10.6.1再訪工廠方法 一、例一:

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() {
      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() {
      public Service getService() {
        return new Implementation2();
      }
    };
}	

public class Factories {
  public static void serviceConsumer(ServiceFactory fact) {
    Service s = fact.getService();
    s.method1();
    s.method2();
  }
  public static void main(String[] args) {
    serviceConsumer(Implementation1.factory);
    // Implementations are completely interchangeable:
    serviceConsumer(Implementation2.factory);
  }
}

二、例二:

interface Game { boolean move(); }
interface GameFactory { Game getGame(); }

class Checkers implements Game {
  private Checkers() {}
  private int moves = 0;
  private static final int MOVES = 3;
  public boolean move() {
	  System.out.println("Checkers move " + moves);
    return ++moves != MOVES;
  }
  public static GameFactory factory = new GameFactory() {
    public Game getGame() { return new Checkers(); }
  };
}	

class Chess implements Game {
  private Chess() {}
  private int moves = 0;
  private static final int MOVES = 4;
  public boolean move() {
	  System.out.println("Chess move " + moves);
    return ++moves != MOVES;
  }
  public static GameFactory factory = new GameFactory() {
    public Game getGame() { return new Chess(); }
  };
}	

public class Games {
  public static void playGame(GameFactory factory) {
    Game s = factory.getGame();
    while(s.move())
      ;
  }
  public static void main(String[] args) {
    playGame(Checkers.factory);
    playGame(Chess.factory);
  }
}

10.7嵌套類

一、若是不須要內部類對象與其外圍類對象之間有聯繫,那麼能夠將內部類聲明爲static.這一般陳偉嵌套類。

二、普通的內部類對象隱式的保存了一個引用,指向建立它的外圍類對象。然而,當內部類 是static時,狀況就以下了:

(1)要建立嵌套類的對象,並不須要其外圍類的對象。

(2)不能從嵌套類的對象訪問非靜態的外圍類對象。

三、普通內部類的字段與方法,只能放在類的外部層次上,因此普通的內部類不能有static數據和static字段,也不能包含嵌套類。可是嵌套類能夠包含全部這些東西:

public class Parcel11 {
  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 ParcelDestination(String whereTo) {
      label = whereTo;
    }
    public String readLabel() { return label; }	
    // Nested classes can contain other static elements:
    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("Tasmania");
  }
}

10.7.1接口內部的類 一、正常狀況下,不能在接口內部放置任何代碼,但嵌套能夠做爲接口的一部分。你放的接口的任何類都自動地是public和static的。

二、若是你想要建立某些公共代碼,使得它們能夠被某個接口的全部實現所公用,那麼使用接口內部類的嵌套類會顯得很方便。

10.7.2從多層嵌套類中訪問外部類的成員

一、一個內部類被嵌套多少層不重要——它能透明地訪問全部它所嵌入的外圍類的全部成員。

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

10.8爲何須要內部類

一、每一個內部類都能獨地繼承自一個(接口的)實現,因此無亂外圍類是否已經繼承了某個(接口的)實現,對於內部類都沒有影響。

二、內部類容許繼承多個非接口類型。

interface A {}
interface B {}

class X implements A, B {}

class Y implements A {
  B makeB() {
    // Anonymous inner class:
    return new B() {};
  }
}

public class MultiInterfaces {
  static void takesA(A a) {}
  static void takesB(B b) {}
  public static void main(String[] args) {
    X x = new X();
    Y y = new Y();
    takesA(x);
    takesA(y);
    takesB(x);
    takesB(y.makeB());
  }
}

三、若是擁有的是抽象的類或具體的類,而不是接口,那就只能使用內部類才能實現多重繼承。

class D {}
abstract class E {}

class Z extends D {
  E makeE() { return new E() {}; }
}

public class MultiImplementation {
  static void takesD(D d) {}
  static void takesE(E e) {}
  public static void main(String[] args) {
    Z z = new Z();
    takesD(z);
    takesE(z.makeE());
  }
}

四、若是使用內部類,還能夠得到其餘一些特性

(1)內部類可使用多個實例,每一個實例都有本身的狀態信息,而且與其外圍類對象的信息相互獨立。

(2)在單個外圍類中,均可以讓多個內部類以不一樣的方式實現同一個接口,或繼承同一個類。

(3)建立內部類對象的時刻並不依賴於外圍類對象的建立。

(4)內部類並無使人迷惑的「is-a」關係,它就是一個獨立的實體。

10.8.1閉包與回調

一、內部類是面向對象的閉包,由於它不只包含外圍類對象(建立內部類的做用域)的信息,還自動擁有一個指向此外圍類對象的引用,在此做用域內,內部類有權操做全部的成員,包括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 {
  public 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;
  public void increment() {
    super.increment();
    i++;
    System.out.println(i);
}
  
private class Closure implements Incrementable {
    public void increment() {
      // Specify outer-class method, otherwise
      // you'd get an infinite recursion:
      Callee2.this.increment();
    }
 }

Incrementable getCallbackReference() {
    return new Closure();
  }
}	

class Caller {
  private Incrementable callbackReference;
  Caller(Incrementable cbh) { callbackReference = cbh; }
  void go() { callbackReference.increment(); }
}

public class Callbacks {
  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();
  }	
}

/* Output:
Other operation
1
1
2
Other operation
2
Other operation
3
*///:~

10.8.2內部類與控制框架

一、設計模式總將變化的事物與保持不變得事物分離開,在這個模式中,模板方法是保持不變的事物,而可覆蓋的方法就是變化的事物。

10.9內部類的繼承

一、那個指向外圍類對象的「祕密」引用必須被初始化,而在導出中再也不存在可鏈接的默認的對象。

class WithInner {
  class Inner {}
}

public class InheritInner extends WithInner.Inner {
  //! InheritInner() {} // Won't compile
  InheritInner(WithInner wi) {
    wi.super();
  }
  public static void main(String[] args) {
    WithInner wi = new WithInner();
    InheritInner ii = new InheritInner(wi);
  }
}

10.10內部類能夠被覆蓋嗎

一、當繼承了某個外圍類的時候,內部類並無發生什麼特別神奇的變化。這兩個內部類是徹底獨立的實體,各自在本身的命名空間內。

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 BigEgg();
  }
} /* Output:
New Egg()
Egg.Yolk()
*///:~

class Egg2 {
  protected class Yolk {
    public Yolk() { System.out.println("Egg2.Yolk()"); }
    public void f() { System.out.println("Egg2.Yolk.f()");}
  }
  private Yolk y = new Yolk();
  public Egg2() { System.out.println("New Egg2()"); }
  public void insertYolk(Yolk yy) { y = yy; }
  public void g() { y.f(); }
}	

public class BigEgg2 extends Egg2 {
  public class Yolk extends Egg2.Yolk {
    public Yolk() { System.out.println("BigEgg2.Yolk()"); }
    public void f() { System.out.println("BigEgg2.Yolk.f()"); }
  }
  public BigEgg2() { insertYolk(new Yolk()); }
  public static void main(String[] args) {
    Egg2 e2 = new BigEgg2();
    e2.g();
  }
} /* Output:
Egg2.Yolk()
New Egg2()
Egg2.Yolk()
BigEgg2.Yolk()
BigEgg2.Yolk.f()
*///:~

10.11局部內部類

一、惟一的理由是,咱們須要一個命名的構造器,或是須要重載構造器,而匿名內部類只能用於實例初始化。因此使用局部類而不使用匿名內部類的另外一個理由是,須要不僅一個該內部類的對象。

interface Counter {
  int next();
}	

public class LocalInnerClass {
  private int count = 0;
  Counter getCounter(final String name) {
    // A local inner class:
    class LocalCounter implements Counter {
      public LocalCounter() {
        // Local inner class can have a constructor
    	  System.out.println("LocalCounter()");
      }
      public int next() {
    	  System.out.println(name); // Access local final
        return count++;
      }
    }
    return new LocalCounter();
  }	
  // The same thing with an anonymous inner class:
  Counter getCounter2(final String name) {
    return new Counter() {
      // Anonymous inner class cannot have a named
      // constructor, only an instance initializer:
      {
    	  System.out.println("Counter()");
      }
      public int next() {
    	  System.out.println(name); // Access local final
        return count++;
      }
    };
  }	
  public static void main(String[] args) {
    LocalInnerClass lic = new LocalInnerClass();
    Counter
      c1 = lic.getCounter("Local inner "),
      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());
  }
} /* Output:
LocalCounter()
Counter()
Local inner 0
Local inner 1
Local inner 2
Local inner 3
Local inner 4
Anonymous inner 5
Anonymous inner 6
Anonymous inner 7
Anonymous inner 8
Anonymous inner 9
*///:~

10.12內部類標識符 $

相關文章
相關標籤/搜索