10.5 在方法和做用域內的內部類java
一、能夠在一個方法裏面或者任意的做用域內定義內部類。這麼作有兩個理由:設計模式
(1)實現某類型的接口,因而能夠建立並返回對其的引用。閉包
(2)要解決一個複雜的問題,想建立一個類來輔助你的解決方案,可是又不但願這個類是共工可用的。 如下是局部內部類:app
public class Parcel5 { public Destination destination(String s) { class PDestination implements Destination { private String label; private PDestination(String whereTo) { label = whereTo; } public String readLabel() { return label; } } return new PDestination(s); } public static void mian(String[] args) { Parcel5 p = new Parcel5(); Destination d = p.destination(""); } }
二、如下的內部類TrackingSlip其實和其餘的類一塊兒編譯過了。然而在定義TrackingSlip的做用域以外,它是不可用的,除此以外,它和普通的類同樣。框架
public class Parcel6 { private void internalTracking(boolean b) { if(b) { class TrackingSlip { private String id; TrackingSlip(String s) { id = s; } String getSlip() { return id; } } TrackingSlip ts = new TrackingSlip("slip"); String s = ts.getSlip(); } // Can't use it here! Out of scope: //! TrackingSlip ts = new TrackingSlip("x"); } public void track() { internalTracking(true); } public static void main(String[] args) { Parcel6 p = new Parcel6(); p.track(); } }
10.6匿名內部類ide
一、使用默認構造器this
public class Parcel7 { public Contents contents() { return new Contents() { private int i = 11; public int value() { return i; } }; } public static void main(String[] args) { Parcel7 p = new Parcel7(); Contents c = p.contents(); System.out.println(c.value()); } }
以上寫法等價於如下寫法:設計
public class Parcel7b { class MyContents implements Contents { private int i = 11; public int value() { return i; } } public Contents contents() { return new MyContents(); } public static void main(String[] args) { Parcel7b p = new Parcel7b(); Contents c = p.contents(); System.out.println(c.value()); } }
二、基類須要一個有參數的構造器code
public class Wrapping { private int i; public Wrapping(int x) { i = x; } public int value() { return i; } } public class Parcel8 { public Wrapping wrapping(int x) { return new Wrapping(x) { public int value() { return super.value() * 47; } }; } public static void main(String[] args) { Parcel8 p = new Parcel8(); Wrapping w = p.wrapping(10); } }
三、若是定義一個匿名內部類,而且但願它使用一個在其外部定義的對象,那麼編譯器會要求其參數時final的。如:對象
public class Parcel9 { public Destination destination(final String dest) { return new Destination() { private String label = dest; public String readLabel() { return label; } }; } public static void main(String[] args) { Parcel9 p = new Parcel9(); Destination d = p.destination("Tasmania"); } }
四、在匿名內部類中不可能有命名構造器(由於它根本沒有名字),但經過實例初始化,就能達到爲匿名內部類建立一個構造器的效果。如:
abstract class Base { public Base(int i) { System.out.print("Base constructor, i = " + i); } public abstract void f(); } public class AnonymousConstructor { public static Base getBase(int i) { return new Base(i) { { System.out.print("Inside instance initializer"); } public void f() { System.out.print("In anonymous f()"); } }; } public static void main(String[] args) { Base base = getBase(47); base.f(); } }
此例中,不要求變量i必定是final的。由於i被傳遞給匿名類的基類的 構造器,它並不會在匿名內部被直接使用。
10.6.1再訪工廠方法 一、例一:
interface Service { void method1(); void method2(); } interface ServiceFactory { Service getService(); } class Implementation1 implements Service { private Implementation1() {} public void method1() {System.out.println("Implementation1 method1");} public void method2() {System.out.println("Implementation1 method2");} public static ServiceFactory factory = new ServiceFactory() { public Service getService() { return new Implementation1(); } }; } class Implementation2 implements Service { private Implementation2() {} public void method1() {System.out.println("Implementation2 method1");} public void method2() {System.out.println("Implementation2 method2");} public static ServiceFactory factory = new ServiceFactory() { public Service getService() { return new Implementation2(); } }; } public class Factories { public static void serviceConsumer(ServiceFactory fact) { Service s = fact.getService(); s.method1(); s.method2(); } public static void main(String[] args) { serviceConsumer(Implementation1.factory); // Implementations are completely interchangeable: serviceConsumer(Implementation2.factory); } }
二、例二:
interface Game { boolean move(); } interface GameFactory { Game getGame(); } class Checkers implements Game { private Checkers() {} private int moves = 0; private static final int MOVES = 3; public boolean move() { System.out.println("Checkers move " + moves); return ++moves != MOVES; } public static GameFactory factory = new GameFactory() { public Game getGame() { return new Checkers(); } }; } class Chess implements Game { private Chess() {} private int moves = 0; private static final int MOVES = 4; public boolean move() { System.out.println("Chess move " + moves); return ++moves != MOVES; } public static GameFactory factory = new GameFactory() { public Game getGame() { return new Chess(); } }; } public class Games { public static void playGame(GameFactory factory) { Game s = factory.getGame(); while(s.move()) ; } public static void main(String[] args) { playGame(Checkers.factory); playGame(Chess.factory); } }
10.7嵌套類
一、若是不須要內部類對象與其外圍類對象之間有聯繫,那麼能夠將內部類聲明爲static.這一般陳偉嵌套類。
二、普通的內部類對象隱式的保存了一個引用,指向建立它的外圍類對象。然而,當內部類 是static時,狀況就以下了:
(1)要建立嵌套類的對象,並不須要其外圍類的對象。
(2)不能從嵌套類的對象訪問非靜態的外圍類對象。
三、普通內部類的字段與方法,只能放在類的外部層次上,因此普通的內部類不能有static數據和static字段,也不能包含嵌套類。可是嵌套類能夠包含全部這些東西:
public class Parcel11 { private static class ParcelContents implements Contents { private int i = 11; public int value() { return i; } } protected static class ParcelDestination implements Destination { private String label; private ParcelDestination(String whereTo) { label = whereTo; } public String readLabel() { return label; } // Nested classes can contain other static elements: public static void f() {} static int x = 10; static class AnotherLevel { public static void f() {} static int x = 10; } } public static Destination destination(String s) { return new ParcelDestination(s); } public static Contents contents() { return new ParcelContents(); } public static void main(String[] args) { Contents c = contents(); Destination d = destination("Tasmania"); } }
10.7.1接口內部的類 一、正常狀況下,不能在接口內部放置任何代碼,但嵌套能夠做爲接口的一部分。你放的接口的任何類都自動地是public和static的。
二、若是你想要建立某些公共代碼,使得它們能夠被某個接口的全部實現所公用,那麼使用接口內部類的嵌套類會顯得很方便。
10.7.2從多層嵌套類中訪問外部類的成員
一、一個內部類被嵌套多少層不重要——它能透明地訪問全部它所嵌入的外圍類的全部成員。
class MNA { private void f() {} class A { private void g() {} public class B { void h() { g(); f(); } } } } public class MultiNestingAccess { public static void main(String[] args) { MNA mna = new MNA(); MNA.A mnaa = mna.new A(); MNA.A.B mnaab = mnaa.new B(); mnaab.h(); } }
10.8爲何須要內部類
一、每一個內部類都能獨地繼承自一個(接口的)實現,因此無亂外圍類是否已經繼承了某個(接口的)實現,對於內部類都沒有影響。
二、內部類容許繼承多個非接口類型。
interface A {} interface B {} class X implements A, B {} class Y implements A { B makeB() { // Anonymous inner class: return new B() {}; } } public class MultiInterfaces { static void takesA(A a) {} static void takesB(B b) {} public static void main(String[] args) { X x = new X(); Y y = new Y(); takesA(x); takesA(y); takesB(x); takesB(y.makeB()); } }
三、若是擁有的是抽象的類或具體的類,而不是接口,那就只能使用內部類才能實現多重繼承。
class D {} abstract class E {} class Z extends D { E makeE() { return new E() {}; } } public class MultiImplementation { static void takesD(D d) {} static void takesE(E e) {} public static void main(String[] args) { Z z = new Z(); takesD(z); takesE(z.makeE()); } }
四、若是使用內部類,還能夠得到其餘一些特性
(1)內部類可使用多個實例,每一個實例都有本身的狀態信息,而且與其外圍類對象的信息相互獨立。
(2)在單個外圍類中,均可以讓多個內部類以不一樣的方式實現同一個接口,或繼承同一個類。
(3)建立內部類對象的時刻並不依賴於外圍類對象的建立。
(4)內部類並無使人迷惑的「is-a」關係,它就是一個獨立的實體。
10.8.1閉包與回調
一、內部類是面向對象的閉包,由於它不只包含外圍類對象(建立內部類的做用域)的信息,還自動擁有一個指向此外圍類對象的引用,在此做用域內,內部類有權操做全部的成員,包括private。
interface Incrementable { void increment(); } // Very simple to just implement the interface: class Callee1 implements Incrementable { private int i = 0; public void increment() { i++; System.out.println(i); } } class MyIncrement { public void increment() { System.out.println("Other operation"); } static void f(MyIncrement mi) { mi.increment(); } } // If your class must implement increment() in // some other way, you must use an inner class: class Callee2 extends MyIncrement { private int i = 0; public void increment() { super.increment(); i++; System.out.println(i); } private class Closure implements Incrementable { public void increment() { // Specify outer-class method, otherwise // you'd get an infinite recursion: Callee2.this.increment(); } } Incrementable getCallbackReference() { return new Closure(); } } class Caller { private Incrementable callbackReference; Caller(Incrementable cbh) { callbackReference = cbh; } void go() { callbackReference.increment(); } } public class Callbacks { public static void main(String[] args) { Callee1 c1 = new Callee1(); Callee2 c2 = new Callee2(); MyIncrement.f(c2); Caller caller1 = new Caller(c1); Caller caller2 = new Caller(c2.getCallbackReference()); caller1.go(); caller1.go(); caller2.go(); caller2.go(); } } /* Output: Other operation 1 1 2 Other operation 2 Other operation 3 *///:~
10.8.2內部類與控制框架
一、設計模式總將變化的事物與保持不變得事物分離開,在這個模式中,模板方法是保持不變的事物,而可覆蓋的方法就是變化的事物。
10.9內部類的繼承
一、那個指向外圍類對象的「祕密」引用必須被初始化,而在導出中再也不存在可鏈接的默認的對象。
class WithInner { class Inner {} } public class InheritInner extends WithInner.Inner { //! InheritInner() {} // Won't compile InheritInner(WithInner wi) { wi.super(); } public static void main(String[] args) { WithInner wi = new WithInner(); InheritInner ii = new InheritInner(wi); } }
10.10內部類能夠被覆蓋嗎
一、當繼承了某個外圍類的時候,內部類並無發生什麼特別神奇的變化。這兩個內部類是徹底獨立的實體,各自在本身的命名空間內。
class Egg { private Yolk y; protected class Yolk { public Yolk() { System.out.println("Egg.Yolk()"); } } public Egg() { System.out.println("New Egg()"); y = new Yolk(); } } public class BigEgg extends Egg { public class Yolk { public Yolk() { System.out.println("BigEgg.Yolk()"); } } public static void main(String[] args) { new BigEgg(); } } /* Output: New Egg() Egg.Yolk() *///:~ class Egg2 { protected class Yolk { public Yolk() { System.out.println("Egg2.Yolk()"); } public void f() { System.out.println("Egg2.Yolk.f()");} } private Yolk y = new Yolk(); public Egg2() { System.out.println("New Egg2()"); } public void insertYolk(Yolk yy) { y = yy; } public void g() { y.f(); } } public class BigEgg2 extends Egg2 { public class Yolk extends Egg2.Yolk { public Yolk() { System.out.println("BigEgg2.Yolk()"); } public void f() { System.out.println("BigEgg2.Yolk.f()"); } } public BigEgg2() { insertYolk(new Yolk()); } public static void main(String[] args) { Egg2 e2 = new BigEgg2(); e2.g(); } } /* Output: Egg2.Yolk() New Egg2() Egg2.Yolk() BigEgg2.Yolk() BigEgg2.Yolk.f() *///:~
10.11局部內部類
一、惟一的理由是,咱們須要一個命名的構造器,或是須要重載構造器,而匿名內部類只能用於實例初始化。因此使用局部類而不使用匿名內部類的另外一個理由是,須要不僅一個該內部類的對象。
interface Counter { int next(); } public class LocalInnerClass { private int count = 0; Counter getCounter(final String name) { // A local inner class: class LocalCounter implements Counter { public LocalCounter() { // Local inner class can have a constructor System.out.println("LocalCounter()"); } public int next() { System.out.println(name); // Access local final return count++; } } return new LocalCounter(); } // The same thing with an anonymous inner class: Counter getCounter2(final String name) { return new Counter() { // Anonymous inner class cannot have a named // constructor, only an instance initializer: { System.out.println("Counter()"); } public int next() { System.out.println(name); // Access local final return count++; } }; } public static void main(String[] args) { LocalInnerClass lic = new LocalInnerClass(); Counter c1 = lic.getCounter("Local inner "), c2 = lic.getCounter2("Anonymous inner "); for(int i = 0; i < 5; i++) System.out.println(c1.next()); for(int i = 0; i < 5; i++) System.out.println(c2.next()); } } /* Output: LocalCounter() Counter() Local inner 0 Local inner 1 Local inner 2 Local inner 3 Local inner 4 Anonymous inner 5 Anonymous inner 6 Anonymous inner 7 Anonymous inner 8 Anonymous inner 9 *///:~
10.12內部類標識符 $