java编程思想 第 10 章 内部类

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/u012417380/article/details/79915645

将一个类的定义放在另一类的定义内部,这就是内部类

一、 创建内部类

把内部类的定义置于外围类的里面:

//: 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

猜你喜欢

转载自blog.csdn.net/u012417380/article/details/79915645
今日推荐