10.5 在方法和作用域内的内部类
1、可以在一个方法里面或者任意的作用域内定义内部类。这么做有两个理由:
(1)实现某类型的接口,于是可以创建并返回对其的引用。
(2)要解决一个复杂的问题,想创建一个类来辅助你的解决方案,但是又不希望这个类是共工可用的。 以下是局部内部类:
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(""); }}
2、以下的内部类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匿名内部类
1、使用默认构造器
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()); }}
2、基类需要一个有参数的构造器
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); }}
3、如果定义一个匿名内部类,并且希望它使用一个在其外部定义的对象,那么编译器会要求其参数时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"); }}
4、在匿名内部类中不可能有命名构造器(因为它根本没有名字),但通过实例初始化,就能达到为匿名内部类创建一个构造器的效果。如:
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再访工厂方法 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); }}
2、例二:
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嵌套类
1、如果不需要内部类对象与其外围类对象之间有联系,那么可以将内部类声明为static.这通常陈伟嵌套类。
2、普通的内部类对象隐式的保存了一个引用,指向创建它的外围类对象。然而,当内部类 是static时,情况就如下了:
(1)要创建嵌套类的对象,并不需要其外围类的对象。
(2)不能从嵌套类的对象访问非静态的外围类对象。
3、普通内部类的字段与方法,只能放在类的外部层次上,所以普通的内部类不能有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接口内部的类 1、正常情况下,不能在接口内部放置任何代码,但嵌套可以作为接口的一部分。你放的接口的任何类都自动地是public和static的。
2、如果你想要创建某些公共代码,使得它们可以被某个接口的所有实现所公用,那么使用接口内部类的嵌套类会显得很方便。
10.7.2从多层嵌套类中访问外部类的成员
1、一个内部类被嵌套多少层不重要——它能透明地访问所有它所嵌入的外围类的所有成员。
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为什么需要内部类
1、每个内部类都能独地继承自一个(接口的)实现,所以无乱外围类是否已经继承了某个(接口的)实现,对于内部类都没有影响。
2、内部类允许继承多个非接口类型。
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()); }}
3、如果拥有的是抽象的类或具体的类,而不是接口,那就只能使用内部类才能实现多重继承。
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()); }}
4、如果使用内部类,还可以获得其他一些特性
(1)内部类可以使用多个实例,每个实例都有自己的状态信息,并且与其外围类对象的信息相互独立。
(2)在单个外围类中,都可以让多个内部类以不同的方式实现同一个接口,或继承同一个类。
(3)创建内部类对象的时刻并不依赖于外围类对象的创建。
(4)内部类并没有令人迷惑的“is-a”关系,它就是一个独立的实体。
10.8.1闭包与回调
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 operation112Other operation2Other operation3*///:~
10.8.2内部类与控制框架
1、设计模式总将变化的事物与保持不变得事物分离开,在这个模式中,模板方法是保持不变的事物,而可覆盖的方法就是变化的事物。
10.9内部类的继承
1、那个指向外围类对象的“秘密”引用必须被初始化,而在导出中不再存在可连接的默认的对象。
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内部类可以被覆盖吗
1、当继承了某个外围类的时候,内部类并没有发生什么特别神奇的变化。这两个内部类是完全独立的实体,各自在自己的命名空间内。
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局部内部类
1、唯一的理由是,我们需要一个命名的构造器,或是需要重载构造器,而匿名内部类只能用于实例初始化。所以使用局部类而不使用匿名内部类的另一个理由是,需要不只一个该内部类的对象。
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 0Local inner 1Local inner 2Local inner 3Local inner 4Anonymous inner 5Anonymous inner 6Anonymous inner 7Anonymous inner 8Anonymous inner 9*///:~
10.12内部类标识符 $