java編程思想 第 10 章 內部類

將一個類的定義放在另一類的定義內部,這就是內部類

一、 創建內部類

把內部類的定義置於外圍類的裏面:

//: innerclasses/Parcel1.java
// Creating inner classes.

public class Parcel1 {
  class Contents {
    private int i = 11;
    public int value() { return i; }
  }
  class Destination {
    private String label;
    Destination(String whereTo) {
      label = whereTo;
    }
    String readLabel() { return label; }
  } 
  // Using inner classes looks just like
  // using any other class, within Parcel1:
  public void ship(String dest) {
    Contents c = new Contents();
    Destination d = new Destination(dest);
    System.out.println(d.readLabel());
  }
  public static void main(String[] args) {
    Parcel1 p = new Parcel1();
    p.ship("Tasmania");
  }
} /* Output:
Tasmania
*///:~

ship()方法使用內部類與使用外部類沒有區別。內部類的名稱嵌套在外部類的裏面。

更多的情況是外部類的方法返回一個內部類的引用。
就像下面的to()和content()方法一樣。

//: innerclasses/Parcel2.java
// Returning a reference to an inner class.

public class Parcel2 {
  class Contents {
    private int i = 11;
    public int value() { return i; }
  }
  class Destination {
    private String label;
    Destination(String whereTo) {
      label = whereTo;
    }
    String readLabel() { return label; }
  }
  public Destination to(String s) {
    return new Destination(s);
  }
  public Contents contents() {
    return new Contents();
  }
  public void ship(String dest) {
    Contents c = contents();
    Destination d = to(dest);
    System.out.println(d.readLabel());
  }
  public static void main(String[] args) {
    Parcel2 p = new Parcel2();
    p.ship("Tasmania");
    Parcel2 q = new Parcel2();
    // Defining references to inner classes:
    Parcel2.Contents c = q.contents();
    Parcel2.Destination d = q.to("Borneo");
  }
} /* Output:
Tasmania
*///:~

如果想從外部類的非靜態方法之外的任意位置創建某個內部類對象。必須像main()方法一樣,具體指明這個對象的類型:OuterClassName.InnerClassName

練習1:

// innerclasses/Outer1.java
// TIJ4 Chapter Innerclasses, Exercise 1, page 347
/* Write a class named Outer that contains an inner class named Innet. 
* Add a method to Outer that returns an object of type Inner. In main(),
* create and initialize a reference to an Inner.
*/

public class Outer1 {
    class Inner { 
        Inner() { System.out.println("Inner()"); }
    }
    Outer1() { System.out.println("Outer1()"); }
    // make an Inner from within a non-static method of outer class:
    Inner makeInner() {
        return new Inner();
    }
    public static void main(String[] args) {
        Outer1 o = new Outer1();
        Inner i = o.makeInner();
    }
}

#output
Outer1()
Inner()

二、鏈接到外部類

當生成一個內部類對象時,內部對象對外部類的所有成員具有訪問權限,沒有任何限制。

//: innerclasses/Sequence.java
// Holds a sequence of Objects.

interface Selector {
  boolean end();
  Object current();
  void next();
}   

public class Sequence {
  private Object[] items;
  private int next = 0;
  public Sequence(int size) { items = new Object[size]; }
  public void add(Object x) {
    if(next < items.length)
      items[next++] = x;
  }
  private class SequenceSelector implements Selector {
    private int i = 0;
    public boolean end() { return i == items.length; }
    public Object current() { return items[i]; }
    public void next() { if(i < items.length) i++; }
  }
  public Selector selector() {
    return new SequenceSelector();
  } 
  public static void main(String[] args) {
    Sequence sequence = new Sequence(10);
    for(int i = 0; i < 10; i++)
      sequence.add(Integer.toString(i));
    Selector selector = sequence.selector();
    while(!selector.end()) {
      System.out.print(selector.current() + " ");
      selector.next();
    }
  }
} /* Output:
0 1 2 3 4 5 6 7 8 9
*///:~

Sequence類內部有一個Object[]數組,爲了訪問該數組,可以通過內部類SequenceSelector.內部類SequenceSelector用到了外部類的items變量,該變量是一個引用。這個就是迭代器設計模式的一個例子。

內部類自動擁有對外部類所有成員的訪問權限。這是因爲外部類對象創建內部類對象時,內部類對象會祕密擁有一個指向外部類對象的引用,使用外部 類的成員時,內部類對象通過外部類對象的引用來訪問外部類的成員。這個細節由編譯器處理。

對於非static修飾的內部類,只能通過外部類對象的一個引用來創建。
練習2:

// innerclasses/Sequence2.java
// TIJ4 Chapter Innerclasses, Exercise 2, page 350
/* Create a class that holds a String, and has a toString() method that
* displays this String. Add several instances of your new class to a 
* Sequence ojbect, then display them.
*/

class Word {
    private String word;
    public Word(String s) { word = s; }
    public String toString() { return word; }
} 

interface Selector {
    boolean end();
    Object current();
    void next();
}

public class Sequence2 {
    private Object[] items;
    private int next = 0;
    public Sequence2(int size) { items = new Object[size]; }
    public void add(Object x) {
        if(next < items.length)
            items[next++] = x;
    }
    private class SequenceSelector implements Selector {
        private int i = 0;
        public boolean end() { return i == items.length; }
        public Object current() { return items[i]; }
        public void next() { if(i < items.length) i++; } 
    }
    public Selector selector() {
        return new SequenceSelector();
    }
    public static void main(String[] args) {
        Sequence2 sequence = new Sequence2(10);
        for(int i = 0; i < 10; i++)
            sequence.add(new Word(Integer.toString(i)));
        Selector selector = sequence.selector();
        while(!selector.end()) {
            System.out.print(selector.current() + " ");
            selector.next();
        }
        Word w1 = new Word("Peace");
        Word w2 = new Word("Love");
        Word w3 = new Word("Easter");
        Sequence2 message = new Sequence2(3);
        message.add(w1);
        message.add(w2);
        message.add(w3);
        Selector sel = message.selector();
        while(!sel.end()) {
            System.out.print(sel.current() + " ");
            sel.next();
        }

    }
}

# output

0 1 2 3 4 5 6 7 8 9 Peace Love Easter

練習3:

// innerclasses/Outer3.java
// TIJ4 Chapter Innerclasses, Exercise 3, page 350
/* Modify Exercise 1 so that Outer has a private String field (initialized
* by the constructor), and Inner has a toString() that displays this field.
* Create an object of type Inner and display it.
*/

public class Outer3 {
    private String s;
    class Inner3 { 
        Inner3() { System.out.println("Inner()"); }
        public String toString() { return s; }
    }
    Outer3(String s) { 
        System.out.println("Outer1()");
        this.s = s;
    }
    Inner3 makeInner3() {
        return new Inner3();
    }
    public static void main(String[] args) {
        Outer3 o = new Outer3("Hi is risen!");
        Inner3 i = o.makeInner3();
        System.out.println(i.toString());
    }
}
# output
Outer1()
Inner()
Hi is risen!

三、使用.this和new

如果需要在內部類中獲取外部類對象的引用,使用外部類名緊跟圓點和this即可獲取外部類對象的正確引用。
例如下列:

//: innerclasses/DotThis.java
// Qualifying access to the outer-class object.

public class DotThis {
  void f() { System.out.println("DotThis.f()"); }
  public class Inner {
    public DotThis outer() {
      return DotThis.this;
      // A plain "this" would be Inner's "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();
  }
} /* Output:
DotThis.f()
*///:~

要想創建一個內部類對象,必須在new表達式中提供對其它外部類對象的引用。這需要.new 語法。

//: innerclasses/DotNew.java
// Creating an inner class directly using the .new syntax.

public class DotNew {
  public class Inner {}
  public static void main(String[] args) {
    DotNew dn = new DotNew();
    DotNew.Inner dni = dn.new Inner();
  }
} ///:~

如果想要直接創建內部類對象,不能使用new new Outer.Inner()創建對象,必須使用外部類對象來創建內類對象。

在擁有外部類對象之前,是不能創建內部類對象的。這是因爲內部類對象創建時必須綁定到一個外部類對象。如果是嵌套類(靜態內部類)則可以直接創建內部類對象。

//: innerclasses/Parcel3.java
// Using .new to create instances of inner classes.

public class Parcel3 {
  class Contents {
    private int i = 11;
    public int value() { return i; }
  }
  class Destination {
    private String label;
    Destination(String whereTo) { label = whereTo; }
    String readLabel() { return label; }
  }
  public static void main(String[] args) {
    Parcel3 p = new Parcel3();
    // Must use instance of outer class
    // to create an instance of the inner class:
    Parcel3.Contents c = p.new Contents();
    Parcel3.Destination d = p.new Destination("Tasmania");
  }
} ///:~

練習4:

// innerclasses/Sequence4.java
// TIJ4 Chapter Innerclasses, Exercise 4, page 352
/* Add a method to the class Sequence.SequenceSelector that produces the 
* reference to the outer class Sequence.
*/

interface Selector {
    boolean end();
    Object current();
    void next();
}

public class Sequence4 {
    private Object[] items;
    private int next = 0;
    // to test SequenceSelector sequence4() in main():
    public void test() { System.out.println("Sequence4.test()"); }
    public Sequence4(int size) { items = new Object[size]; }
    public void add(Object x) {
        if(next < items.length)
            items[next++] = x;
    }
    private class SequenceSelector implements Selector {
        private int i = 0;
        public boolean end() { return i == items.length; }
        public Object current() { return items[i]; }
        public void next() { if(i < items.length) i++; } 
        // method to produce outer class reference:
        public Sequence4 sequence4() { return Sequence4.this; }
    }
    public Selector selector() {
        return new SequenceSelector();
    }
    public static void main(String[] args) {
        Sequence4 sequence = new Sequence4(10);
        for(int i = 0; i < 10; i++)
            sequence.add(Integer.toString(i));
        Selector selector = sequence.selector();
        while(!selector.end()) {
            System.out.print(selector.current() + " ");
            selector.next();
        }
        // cast and test:
        ((SequenceSelector)selector).sequence4().test();    
    }
}

四、內部類和向上轉型

將內部類向上轉型爲其父類或實現的接口時,能夠獲得指向內部類父類或接口的引用。隱藏內部類實現的細節。

兩個接口:

//: innerclasses/Contents.java
public interface Contents {
  int value();
} ///:~


//: innerclasses/Destination.java
public interface Destination {
  String readLabel();
} ///:~

Contents和Destination表示客戶端程序員可用的接口。(接口所有成員被自動設爲public)
當取得了一個指向基類或接口的引用時,甚至無法找出它確切的類型

//: innerclasses/TestParcel.java

class Parcel4 {
  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 label; }
  }
  public Destination destination(String s) {
    return new PDestination(s);
  }
  public Contents contents() {
    return new PContents();
  }
}

public class TestParcel {
  public static void main(String[] args) {
    Parcel4 p = new Parcel4();
    Contents c = p.contents();
    Destination d = p.destination("Tasmania");
    // Illegal -- can't access private class:
    //! Parcel4.PContents pc = p.new PContents();
  }
} ///:~

Parcel4有兩個內部類:內部類PCcontents是private ,所以該類只能在Parcel4類中被訪問,其它類無法訪問。PDestination類是Protected,所以它只能被Parcel4以及Parcel4子類、同一個包中的類訪問

練習6:

// innerclasses/Ex6.java
// TIJ4 Chapter Innerclasses, Exercise 6, page 353
/* Create an interface with at least one method, in its own package. Create
* a class in a separate package. Add a protected inner class that 
* implements the interface. In a third package, inherit from your class and
* inside a method, return an object of the protected inner class, upcasting
* to the interface during the return.
*/

/* // in separate package:
* public interface Ex6Interface {
*   String say();
* }
*
* // and in a second package:
* public class Ex6Base {
*   protected class Ex6BaseInner implements Ex6Interface {
*       // need public constructor to create one in Ex6Base child: 
*       public Ex6BaseInner() { }
*       public String say() { return "Hi"; }
*   }
* }
*/ 

import innerclasses.ex6Interface.*;
import innerclasses.ex6Base.*;

public class Ex6 extends Ex6Base {
    Ex6Interface getBaseInner() { 
        return this.new Ex6BaseInner();
    }
    public static void main(String[] args) {
        Ex6 ex = new Ex6();
        System.out.println(ex.getBaseInner().say());
    }   
}

練習7:

// innerclasses/Outer7.java
// TIJ4 Chapter Innerclasses, Exercise 7, page 354
/* Create a class with a private field and a private method. Create an
* inner class with a method that modifies the outer-class field and calls
* the outer class method. In a second outer-class method, create an object
* of the inner class and call its method, then show the effect on the
* outer-class object. 
*/

class Outer7 {
    private int oi = 1;
    private void hi() { System.out.println("Outer hi"); }
    class Inner {
        void modifyOuter() { 
            oi *= 2;
            hi(); 
        }
    }
    public void showOi() { System.out.println(oi); }
    void testInner() {
        Inner in = new Inner();
        in.modifyOuter();
    }
    public static void main(String[] args) {
        Outer7 out = new Outer7();
        out.showOi();
        out.testInner();
        out.showOi();
    }
}

練習8:

// innerclasses/Outer8.java
// TIJ4 Chapter Innerclasses, Exercise 8, page 354
/* Determine whether an outer class has access to the private elements of 
* its inner class. 
*/

class Outer8 {  
    class Inner {
        private int ii1 = 1;
        private int ii2 = 2;
        private void showIi2() { System.out.println(ii2); }
        private void hi() { System.out.println("Inner hi"); }
        }
    // Need to create objects to access private elements of Inner:
    int oi = new Inner().ii1;
    void showOi() { System.out.println(oi); }
    void showIi2() { new Inner().showIi2(); } 
    void outerHi() { new Inner().hi(); }
    public static void main(String[] args) {
        Outer8 out = new Outer8();
        out.showOi();
        out.showIi2();
        out.outerHi();
    }
}

外部類可以訪問內部類的private 元素

五、在方法和作用域內部的內部類
在方法的作用域內創建一個類,該類被稱爲局部內部類。

//: innerclasses/Parcel5.java
// Nesting a class within a method.

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 main(String[] args) {
    Parcel5 p = new Parcel5();
    Destination d = p.destination("Tasmania");
    System.out.println(d.readLabel());
  }
} ///:~

#output
Tasmania

PDestination類是destination方法的一部分,destination方法執行完畢後,d 仍持有該局部內部類對象。
任意作用域內嵌入一個內部類:

//: innerclasses/Parcel6.java
// Nesting a class within a scope.

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

該類在定義TrackingingShip的作用域之外,是不可用,除此之外,和普通類沒有什麼不同。
練習9

// innerclasses/Ex9.java
// TIJ4 Chapter Innerclasses, Exercise 9, page 356
/* Create an interface with at least one method, and implement that
* interface by defining an inner class within a method, which returns a
* reference to your interface.
*/

interface Ex9Interface {
    void say(String s); 
}

public class Ex9 {  
    Ex9Interface f() {
        class Inner implements Ex9Interface {
            public void say(String s) {
                System.out.println(s); 
            }
        }
        return new Inner();
    }
    public static void main(String[] args) {
        Ex9 x = new Ex9();
        x.f().say("hi");
    } 
}

練習10

// innerclasses/Ex10.java
// TIJ4 Chapter Innerclasses, Exercise 10, page 356
/* Repeat the previous exercise but define the inner class within a
* scope with scope within a method.
*/

interface Ex10Interface {
    void say(String s); 
}

public class Ex10 { 
    Ex10Interface f(boolean b) {
        if(b) {
            class Inner implements Ex10Interface {
                public void say(String s) {
                    System.out.println(s); 
                }
            }
            return new Inner();
        }
        return null;
    }
    public static void main(String[] args) {
        Ex10 x = new Ex10();
        x.f(true).say("hi");
    } 
}

練習11:

// innerclasses/Ex11.java
// TIJ4 Chapter Innerclasses, Exercise 11, page 356
/* Create a private inner class that implements a public interface.
* Write a method that returns a reference to an instance of the private
* inner class, upcast to the interface. Show that the inner class is 
* completely hidden by trying to downcast to it.
*/

/* public interface Ex11Interface {
*   void say(String s); 
* }
*/

class Test {
    private class Inner implements Ex11Interface {
        public void say(String s) {
            System.out.println(s); 
        }
    }
    Ex11Interface f() {
        return new Inner();
    }
}
public class Ex11 { 
    public static void main(String[] args) {
        Test t = new Test();
        t.f().say("hi");
        // Error: cannot find symbol: class Inner:
        // ((Inner)t.f()).say("hello");
    } 
}

六、匿名內部類

//: innerclasses/Parcel7.java
// Returning an instance of an anonymous inner class.

public class Parcel7 {
  public Contents contents() {
    return new Contents() { // Insert a class definition
      private int i = 11;
      public int value() { return i; }
    }; // Semicolon required in this case
  }
  public static void main(String[] args) {
    Parcel7 p = new Parcel7();
    Contents c = p.contents();
  }
} ///:~

Content是一個接口,創建匿名內部類實現Content接口並在後面緊跟該匿名內部類的定義。
上面是以下創建對象的簡潔方式

//: innerclasses/Parcel7b.java
// Expanded version of Parcel7.java

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

上面的匿名內部中,使用默認的構造器來生成Contents。如果基類需要有參構造函數呢?

//: innerclasses/Parcel8.java
// Calling the base-class constructor.

public class Parcel8 {
  public Wrapping wrapping(int x) {
    // Base constructor call:
    return new Wrapping(x) { // Pass constructor argument.
      public int value() {
        return super.value() * 47;
      }
    }; // Semicolon required
  }
  public static void main(String[] args) {
    Parcel8 p = new Parcel8();
    Wrapping w = p.wrapping(10);
  }
} ///:~

只需要傳遞參數即可。Wrapping只是一個具有具體實現的普通類,但他仍被當作公共接口來使用

//: innerclasses/Wrapping.java
public class Wrapping {
  private int i;
  public Wrapping(int x) { i = x; }
  public int value() { return i; }
} ///:~

匿名內部類後面的分號是表示表達式的結束。

在匿名內部類中定義字段時,能夠對其執行初始化操作。

//: innerclasses/Parcel9.java
// An anonymous inner class that performs
// initialization. A briefer version of Parcel5.java.

public class Parcel9 {
  // Argument must be final to use inside
  // anonymous inner class:
  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");
  }
} ///:~

如果定義一個匿名內部類,並且希望它使用一個在外部定義的對象,那麼編譯器要求其參數引用是final的。

匿名內部類不可能有構造器,但可以通過實例初始化達到構造器效果。

//: innerclasses/AnonymousConstructor.java
// Creating a constructor for an anonymous inner class.
import static net.mindview.util.Print.*;

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

public class AnonymousConstructor {
  public static Base getBase(int i) {
    return new Base(i) {
      { print("Inside instance initializer"); }
      public void f() {
        print("In anonymous f()");
      }
    };
  }
  public static void main(String[] args) {
    Base base = getBase(47);
    base.f();
  }
} /* Output:
Base constructor, i = 47
Inside instance initializer
In anonymous f()
*///:~

在上面實例中,不要求變量i爲final的,因爲它被傳遞給匿名內部類的基類構造器使用,而不是被匿名內部類直接使用。

destination方法的參數必須爲final的。

//: innerclasses/Parcel10.java
// Using "instance initialization" to perform
// construction on an anonymous inner class.

public class Parcel10 {
  public Destination
  destination(final String dest, final float price) {
    return new Destination() {
      private int cost;
      // Instance initialization for each object:
      {
        cost = Math.round(price);
        if(cost > 100)
          System.out.println("Over budget!");
      }
      private String label = dest;
      public String readLabel() { return label; }
    };
  } 
  public static void main(String[] args) {
    Parcel10 p = new Parcel10();
    Destination d = p.destination("Tasmania", 101.395F);
  }
} /* Output:
Over budget!
*///:~

匿名內部類即可擴展類,也可擴展接口,但不能兩者兼備。
練習14:

// innerclasses/HorrorShow14.java
// TIJ4 Chapter Innerclasses, Exercise 14, page361
/* Modify interfaces/HorrorShow.java to implement DangerousMonster and
* Vampire using anonymous classes.
*/
import static org.greggordon.tools.Print.*;

interface Monster {
    void menace();
}   

interface DangerousMonster extends Monster {
    void destroy();
}   

interface Lethal {
    void kill();
}

class DragonZilla implements DangerousMonster {
    public void menace() {}
    public void destroy() {}
}

interface Vampire extends DangerousMonster, Lethal {
    void drinkBlood();
}

class VeryBadVampire implements Vampire {
    public void menace() {}
    public void destroy() {}
    public void kill() {}
    public void drinkBlood() {} 
}

public class HorrorShow14 {
    static void u(Monster b) { b.menace(); }
    static void v(DangerousMonster d) {
        d.menace();
        d.destroy();
    }
    static void w(Lethal l) { l.kill(); }
    public DangerousMonster monsterMaker() {
        return new DangerousMonster() {
            public void menace() { println("DangerousMonster Menace"); }
            public void destroy() { println("DangerousMonster Destroy"); }
        };
    }
    public Vampire vampireMaker() {
        return new Vampire() {
            public void menace() { println("Vampire Menace"); }
            public void destroy() { println("Vampire Destroy"); }
            public void kill() { println("Vampire Kill"); }
            public void drinkBlood() { println("Vampire DrinkBlood"); }
        };
    }       
    public static void main(String[] args) {
        HorrorShow14 show = new HorrorShow14();
        show.u(show.monsterMaker());
        show.v(show.monsterMaker());
        show.u(show.vampireMaker());
        show.v(show.vampireMaker());
        show.w(show.vampireMaker());
    }
}

練習15:

// innerclasses/Ex15.java
// TIJ4 Chapter Innerclasses, Exercise 15, page361
/* Create a class with a non-default constructor and no default constructor.
* Create a second class that has a method that returns a reference to an
* object of the first class. Create the object that you return by making an
* anonymous inner class that inherits from the first class. 
*/

class One {
    private String s;
    One(String s) { this.s = s; } 
    public String showS() { return s; }
}

public class Ex15 {
    public One makeOne(String s) {
        return new One(s) { };
    }
    public static void main(String[] args) {
        Ex15 x = new Ex15();
        System.out.println(x.makeOne("hi").showS());
    }
}
#output

hi

6.1 再訪工程方法

使用匿名內部類,interfaces/Factories.java.

//: innerclasses/Factories.java
import static net.mindview.util.Print.*;

interface Service {
  void method1();
  void method2();
}

interface ServiceFactory {
  Service getService();
}   

class Implementation1 implements Service {
  private Implementation1() {}
  public void method1() {print("Implementation1 method1");}
  public void method2() {print("Implementation1 method2");}
  public static ServiceFactory factory =
    new ServiceFactory() {
      public Service getService() {
        return new Implementation1();
      }
    };
}   

class Implementation2 implements Service {
  private Implementation2() {}
  public void method1() {print("Implementation2 method1");}
  public void method2() {print("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);
  }
} /* Output:
Implementation1 method1
Implementation1 method2
Implementation2 method1
Implementation2 method2
*///:~

同理

//: innerclasses/Games.java
// Using anonymous inner classes with the Game framework.
import static net.mindview.util.Print.*;

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() {
    print("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() {
    print("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);
  }
} /* Output:
Checkers move 0
Checkers move 1
Checkers move 2
Chess move 0
Chess move 1
Chess move 2
Chess move 3
*///:~

七、嵌套類

不需要內部類對象和外圍類對象之間有聯繫,可將內部類聲明爲static,這種類稱爲嵌套類(nested class)。普通內部類對象內部隱式的保存了一個指向外圍類對象的引用。而static修飾的嵌套類: 1.創建嵌套類時,並不需要外圍類對象。

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

嵌套類與普通內部類的另一個區別是:普通內部類的字段和方法不能是靜態的。(可以有static final 常量,不能有嵌套類。而嵌套類可以

//: innerclasses/Parcel11.java
// Nested classes (static inner classes).

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");
  }
} ///:~

main()方法中沒有創建Parcel11 對象,直接調用靜態方法生成了嵌套類對象。

嵌套類對象中沒有this引用外圍類對象。
練習18:

// innerclasses/Ex18.java
// TIJ4 Chapter Innerclasses, Exercise 18, page 366
/* Create a class containing a nested class. In main(), create an instance of
* the nested class.
*/

public class Ex18 {
    Ex18() { System.out.println("Ex18()"); }
    public static class Ex18Nest1 {
        Ex18Nest1() { System.out.println("Ex18Nest1()"); }
    }
    private static class Ex18Nest2 {
        Ex18Nest2() { System.out.println("Ex18Nest2()"); }
    }
    public static void main(String[] args) {
        Ex18Nest1 en1 = new Ex18Nest1();
        Ex18Nest2 en2 = new Ex18Nest2();
    }
}

練習19:

// innerclasses/Ex19.java
// TIJ4 Chapter Innerclasses, Exercise 19, page 366
/* Create a class containing an inner class that itself contains an inner
* class. Repeat this using nested classes. Note the names of the .class files
* produced by the compiler.
*/

public class Ex19 {
    Ex19() { System.out.println("Ex19()"); }
    private class Ex19Inner {
        Ex19Inner() { System.out.println("Ex19Inner()"); } 
        private class Ex19InnerInner {
            Ex19InnerInner() {
                System.out.println("Ex19InnerInner()");
            }
        }
    }
    private static class Ex19Nested {
        Ex19Nested() { System.out.println("Ex19Nested()"); }
        private static class Ex19NestedNested {
            Ex19NestedNested() { 
            System.out.println("Ex19NestedNested()"); 
            }
        }
    }   
    public static void main(String[] args) {
        Ex19Nested en = new Ex19Nested();
        Ex19Nested.Ex19NestedNested enn = new Ex19Nested.Ex19NestedNested();
        Ex19 e19 = new Ex19();
        Ex19.Ex19Inner ei = e19.new Ex19Inner();
        Ex19.Ex19Inner.Ex19InnerInner eii = ei.new Ex19InnerInner();
    }
}

/* compiler produces:
* Ex19$Ex19Inner$Ex19InnerInner.class
* Ex19$Ex19Inner.class
* Ex19$Ex19Nested$Ex19NestedNested.class
* Ex19$Ex19Nested.class
* Ex19.class
*/

7.1 接口內部的類

正常情況下,不能在接口內部放置任何代碼,但嵌入類可以放置在接口中。放入接口中的任何類都是public和static的。因爲類是static的,只是將嵌套類置於接口命名空間內,並不違反接口的規則。
可以在內部類實現外部接口:

//: innerclasses/ClassInInterface.java
// {main: ClassInInterface$Test}

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();
    }
  }
} /* Output:
Howdy!
*///:~

如果希望創建某些公共代碼,使得它們可以被接口的所有不同實現類使用,可以把代碼放在接口的嵌套類中。

將用於測試的main方法放在嵌套類中減少額外的測試代碼

//: innerclasses/TestBed.java
// Putting test code in a nested class.
// {main: TestBed$Tester}

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();
    }
  }
} /* Output:
f()
*///:~

練習20:

// innerclasses/Ex20.java
// TIJ4 Chapter Innerclasses, Exercise 20, page 367
/* Create an interface containing a nested class. Implement this interface and 
* create an instance of the nested class.
*/

interface In {
    class Nested {
        Nested() { System.out.println("Nested()"); }
        public void hi() { System.out.println("hi"); }      
    }
}

public class Ex20 implements In {
    public static void main(String[] args) {
        In.Nested in = new In.Nested();
        in.hi();                
    }
}

練習21:

// innerclasses/Ex21.java
// TIJ4 Chapter Innerclasses, Exercise 21, page 367
/* Create an interface that contains a nested class that has a static method that
* calls the methods of your interface and displays the results. Implement your
* interface and pass an instance of your implementation to the method. 
*/

interface In {
    String f();
    String g(); 
    class Nested {
        static void testIn(In i) { 
            System.out.println(i.f() + i.g());
        }       
    }       
}

public class Ex21 implements In {
    public String f() { return "hello "; }
    public String g() { return "friend"; }
    public static void main(String[] args) {
        Ex21 x = new Ex21();
        In.Nested.testIn(x);                        
    }
}

7.2 從多重嵌套類中訪問外部類的成員

//: innerclasses/MultiNestingAccess.java
// Nested classes can access all members of all
// levels of the classes they are nested within.

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

.new 不必在調用構造器時限定全類名。

八、爲什麼需要內部類

1.多重繼承,內部類可以對同一接口進行不同實現

練習23:

// innerclasses/Ex23.java
// TIJ4 Chapter Innerclasses, Exercise 23, page 371
/* Create an interface U with three methods. Create a class A with a method that
* produces a reference to a U by building an anonymous inner class. Create a second
* class B that contains an array of U. B should have one method that accepts and 
* stores a reference to U in the array, a second method that sets a reference in
* the array (specified by the method argument) to null, and a third method that
* moves through the array and calls the methods in U. In main, create a group of A 
* objects and a single B. Fill the B with U references produced by the A objects. 
* Use the B to call back into all the A objects. Remove some of the U references
* from the B.
*/

interface U {
    void f();
    void g();
    String toString();
}

class A {
    U buildU() {
        return new U() {
            public void f() { System.out.println("f()"); }
            public void g() { System.out.println("g()"); }
            public String toString() { return "I'm a U"; }
        };
    }
}

class B {
    private U[] us;
    B(int i) {
        us = new U[i];
    }
    void addU(U u, int i) {
        us[i] = u;
    }
    void eraseU(int i) {
        us[i] = null;   
    }
    void testUs() {
        for(U u : us) {
            u.f();
            u.g();
            u.toString();
        }       
    }
    void showUs() {
        for(U u : us) { 
            if(u != null) System.out.println(u.toString());
            else System.out.println("I'm null");
        }
    }
}

public class Ex23 {
    public static void main(String[] args) {
        A a0 = new A();
        A a1 = new A();
        A a2 = new A();
        B b = new B(3);
        b.addU(a0.buildU(), 0);
        b.addU(a1.buildU(), 1);
        b.addU(a2.buildU(), 2);
        b.showUs();
        b.testUs();
        b.eraseU(0);
        b.eraseU(1);
        b.showUs();
    }
}

8.1 閉包與回調

//: innerclasses/Callbacks.java
// Using inner classes for callbacks
package innerclasses;
import static net.mindview.util.Print.*;

interface Incrementable {
  void increment();
}

// Very simple to just implement the interface:
class Callee1 implements Incrementable {
  private int i = 0;
  public void increment() {
    i++;
    print(i);
  }
}   

class MyIncrement {
  public void increment() { print("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++;
    print(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
*///:~

8.2 內部類與控制框架

相應事件的驅動系統被稱爲事件驅動系統。

//: innerclasses/controller/Event.java
// The common methods for any control event.
package innerclasses.controller;

public abstract class Event {
  private long eventTime;
  protected final long delayTime;
  public Event(long delayTime) {
    this.delayTime = delayTime;
    start();
  }
  public void start() { // Allows restarting
    eventTime = System.nanoTime() + delayTime;
  }
  public boolean ready() {
    return System.nanoTime() >= eventTime;
  }
  public abstract void action();
} ///:~
//: innerclasses/controller/Controller.java
// The reusable framework for control systems.
package innerclasses.controller;
import java.util.*;

public class Controller {
  // A class from java.util to hold Event objects:
  private List<Event> eventList = new ArrayList<Event>();
  public void addEvent(Event c) { eventList.add(c); }
  public void run() {
    while(eventList.size() > 0)
      // Make a copy so you're not modifying the list
      // while you're selecting the elements in it:
      for(Event e : new ArrayList<Event>(eventList))
        if(e.ready()) {
          System.out.println(e);
          e.action();
          eventList.remove(e);
        }
  }
} ///:~

控制溫室的運作

//: innerclasses/GreenhouseControls.java
// This produces a specific application of the
// control system, all in a single class. Inner
// classes allow you to encapsulate different
// functionality for each type of event.
import innerclasses.controller.*;

public class GreenhouseControls extends Controller {
  private boolean light = false;
  public class LightOn extends Event {
    public LightOn(long delayTime) { super(delayTime); }
    public void action() {
      // Put hardware control code here to
      // physically turn on the light.
      light = true;
    }
    public String toString() { return "Light is on"; }
  } 
  public class LightOff extends Event {
    public LightOff(long delayTime) { super(delayTime); }
    public void action() {
      // Put hardware control code here to
      // physically turn off the light.
      light = false;
    }
    public String toString() { return "Light is off"; }
  }
  private boolean water = false;
  public class WaterOn extends Event {
    public WaterOn(long delayTime) { super(delayTime); }
    public void action() {
      // Put hardware control code here.
      water = true;
    }
    public String toString() {
      return "Greenhouse water is on";
    }
  } 
  public class WaterOff extends Event {
    public WaterOff(long delayTime) { super(delayTime); }
    public void action() {
      // Put hardware control code here.
      water = false;
    }
    public String toString() {
      return "Greenhouse water is off";
    }
  }
  private String thermostat = "Day";    
  public class ThermostatNight extends Event {
    public ThermostatNight(long delayTime) {
      super(delayTime);
    }
    public void action() {
      // Put hardware control code here.
      thermostat = "Night";
    }
    public String toString() {
      return "Thermostat on night setting";
    }
  } 
  public class ThermostatDay extends Event {
    public ThermostatDay(long delayTime) {
      super(delayTime);
    }
    public void action() {
      // Put hardware control code here.
      thermostat = "Day";
    }
    public String toString() {
      return "Thermostat on day setting";
    }
  }
  // An example of an action() that inserts a
  // new one of itself into the event list:
  public class Bell extends Event {
    public Bell(long delayTime) { super(delayTime); }
    public void action() {
      addEvent(new Bell(delayTime));
    }
    public String toString() { return "Bing!"; }
  } 
  public class Restart extends Event {
    private Event[] eventList;
    public Restart(long delayTime, Event[] eventList) {
      super(delayTime);
      this.eventList = eventList;
      for(Event e : eventList)
        addEvent(e);
    }
    public void action() {
      for(Event e : eventList) {
        e.start(); // Rerun each event
        addEvent(e);
      }
      start(); // Rerun this Event
      addEvent(this);
    }
    public String toString() {
      return "Restarting system";
    }
  } 
  public static class Terminate extends Event {
    public Terminate(long delayTime) { super(delayTime); }
    public void action() { System.exit(0); }
    public String toString() { return "Terminating";  }
  }
} ///:~
//: innerclasses/GreenhouseController.java
// Configure and execute the greenhouse system.
// {Args: 5000}
import innerclasses.controller.*;

public class GreenhouseController {
  public static void main(String[] args) {
    GreenhouseControls gc = new GreenhouseControls();
    // Instead of hard-wiring, you could parse
    // configuration information from a text file here:
    gc.addEvent(gc.new Bell(900));
    Event[] eventList = {
      gc.new ThermostatNight(0),
      gc.new LightOn(200),
      gc.new LightOff(400),
      gc.new WaterOn(600),
      gc.new WaterOff(800),
      gc.new ThermostatDay(1400)
    };  
    gc.addEvent(gc.new Restart(2000, eventList));
    if(args.length == 1)
      gc.addEvent(
        new GreenhouseControls.Terminate(
          new Integer(args[0])));
    gc.run();
  }
} /* Output:
Bing!
Thermostat on night setting
Light is on
Light is off
Greenhouse water is on
Greenhouse water is off
Thermostat on day setting
Restarting system
Terminating
*///:~

九、內部類的繼承

內部類的構造器必須連接到外圍類對象的引用

//: innerclasses/InheritInner.java
// Inheriting an inner class.

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);
  }
} ///:~

十、內部類可被覆蓋嗎?

//: innerclasses/BigEgg.java
// An inner class cannot be overriden like a method.
import static net.mindview.util.Print.*;

class Egg {
  private Yolk y;
  protected class Yolk {
    public Yolk() { print("Egg.Yolk()"); }
  }
  public Egg() {
    print("New Egg()");
    y = new Yolk();
  }
}   

public class BigEgg extends Egg {
  public class Yolk {
    public Yolk() { print("BigEgg.Yolk()"); }
  }
  public static void main(String[] args) {
    new BigEgg();
  }
} /* Output:
New Egg()
Egg.Yolk()
*///:~

子類中的內部類不能覆蓋父類中的內部類,它們在各自的命名空間中

//: innerclasses/BigEgg2.java
// Proper inheritance of an inner class.
import static net.mindview.util.Print.*;

class Egg2 {
  protected class Yolk {
    public Yolk() { print("Egg2.Yolk()"); }
    public void f() { print("Egg2.Yolk.f()");}
  }
  private Yolk y = new Yolk();
  public Egg2() { print("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() { print("BigEgg2.Yolk()"); }
    public void f() { print("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()
*///:~

十一 、局部內部類

局部內部類在代碼塊裏創建,典型的在方法中創建。局部內部類不能有訪問修飾符,它不是外圍類的一部分。局部內部類可以訪問當前代碼塊中的常量和外圍類的所有成員變量。

//: innerclasses/LocalInnerClass.java
// Holds a sequence of Objects.
import static net.mindview.util.Print.*;

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
        print("LocalCounter()");
      }
      public int next() {
        printnb(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:
      {
        print("Counter()");
      }
      public int next() {
        printnb(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++)
      print(c1.next());
    for(int i = 0; i < 5; i++)
      print(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
*///:~

十二 內部類標識符

外圍$內部類
匿名內部類以數字作爲標識符。

Counter.class
LocalInnerClass$1
LocalInnerClass.class
LocalInnerClass$1LocalCounter.class
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章