From Thinking in Java 4th Editionjava
內部類安全
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.readLable()); } public static void main(String[] args) { Parcel1 p = new Parcel1(); p.ship("Tasmania"); } } /* Output: Tasmania */
更典型的狀況是,外部類將有一個方法,該方法返回一個指向內部類的引用閉包
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 */
當生成一個內部類的對象時,此對象與製造它的外圍對象之間就有了聯繫,它能訪問其外圍對象的全部成員。內部類擁有其外圍類的全部元素的訪問權:app
interface Selector { boolean end(); Object current(); void next(); } public class Sequence { private Object[] items; private int next = 0; public Sequence(int size) { item = 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.println(selector.current() + " "); selector.next(); } } } /* Output: 0 1 2 3 4 5 6 7 8 9 */
要生成對外部類對象的引用,可使用外部類的名字後面緊跟圓點和this:框架
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語法:ide
public class DotNew { public class Inner {} public static void main(String[] args){ DotNew dn = new DotNew(); DotNew.Inner dni = dn.new Inner(); } }
要直接建立內部類的對象,你不能按照想象的方式,去引用外部類的名字DotNew,而必須使用外部類的對象來建立該內部類對象.測試
在擁有外部類對象以前是不可能建立內部類對象的。這是由於內部類對象會暗暗地鏈接到建立它的外部類對象上。ui
但若是你建立的是嵌套類(靜態內部類),那就不須要對外部類對象的引用:this
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"); } }
內部類與向上轉型spa
示例接口:
public interface Destination { String readLabel(); } public interface Contents { int value(); }
當取得一個指向基類或接口的引用時,甚至可能沒法找出它的確切類型:
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 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(); } }
能夠在一個方法裏或者在任意的做用域內定義內部類。
在方法的做用域內建立一個完整的內,稱做局部內部類:
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"); } }
1. PDestination類是destination()方法的一部分,因此在destination()方法以外不能訪問PDestination。注意出如今return後的向上轉型。
2. 在destination()方法內定義內部類,並不意味着在destination()方法執行完畢以後,PDestination就不可用了。
3. 能夠在同一個子目錄下的任意類中,對某個內部類用標示符PDestination命名,這並不會有名字衝突。
在任意的做用域內嵌入一個內部類:
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("X"); 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(); } }
TrackingSlip類被嵌入if語句,並非說該類的建立是有條件的:
1. 它實際上是與別的類一塊兒被編譯過了
2. 在定義TrackingSlip的做用域以外,它是不可用的
匿名內部類
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(); } }
這裏,Contents是以前定義的接口。這種奇怪的語法指的是:「建立一個繼承自Contents的匿名類的對象」,經過new表達式返回的引用被向上轉型爲對Contents的引用,上述匿名內部類的語法是下述形式的簡化形式:
public class Parcel7b { class MyContents implements Contents { private 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,下面展現,若是你的基類須要一個有參數的構造器應該怎麼辦:
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) { // Base constructor call: return new Wrapping(x) { // Pass constructor argument public int value(){ return super.value() * 47; } }; // Semicolon required } public static void main(Sring[] args){ Parcel8 p = new Parcel8(); Wrapping w = p.wrapping(10); } }
若是定義一個匿名內部類,而且但願它使用一個在其外部定義的對象,那麼編譯器會要求其參數引用是final的:
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"); } }
若是想作一些相似構造器的行爲,在匿名類中不可能有命名構造器(由於它根本沒有名字!),但經過實例初始化,就可以達到匿名內部類建立一個構造器的效果。
目標效果:
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 = new getBase(47); base.f(); } } /* Output: Base constructor, i = 47 Inside instance initializer In anonymous f() */
實例初始化的「parcel」 形式:
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! */
運用匿名內部類實現工廠方法:
import static net.mindview.util.Print.*; interface Service { void method1(); void method2(); } interface ServiceFactory { Service getService(); } class Implementation1 implements Service { private Implementation1() {} public method1 { print("Implementation1 method1");} public method2 { print("Implementation1 method2");} public static ServiceFactory factory = new ServiceFactory() { public Service getService(){ return new Implementation1(); } } } class Implementation2 implements Service { private Implementation2() {} public method1 { print("Implementation2 method1");} public method2 { print("Implementation2 method2");} public static ServiceFactory factory = new ServiceFactory(){ public 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 */
Game類也能夠經由一樣的改進:
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 */
嵌套類
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 = new contents(); Destination d = destination("Tasmania"); } }
正常狀況下,不能在接口內放置任何代碼,但嵌套類能夠做爲接口的一部分。放到接口中的任何類都是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! */
要運行以上代碼,執行java ClassInInterface$Test便可,在Unix/Linux中,符號$必須轉義。
可使用嵌套類來放置測試代碼:(運行這個程序,執行java TestBed$Tester便可。)
//: 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()
*/
這生成了一個獨立的類TestBed$Tester。 可使用這個類來作測試,但沒必要在發佈的產品中包含它,在將產品打包前能夠簡單地刪除TestBed$Tester.class文件。
一個內部類被嵌套多少層並不重要,它能透明地訪問全部它所嵌入的外圍類的全部成員:
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(); } }
內部類使得多重繼承的解決方案變得完整。接口解決了部分問題,而內部類有效地實現了「多重繼承」。也就是說,內部類容許繼承多個非接口類。讓咱們考慮這樣一種情形:必須在一個類中以某種方式實現兩個接口。有兩種方式:
1. 使用單一類
2. 使用內部類
package innerclasses; interface A {} interface B {}
// 1. Use only one class class X implements A, B {}
// 2. Use inner class 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()); } }
若是擁有的是抽象的類或具體的類,而不是接口, 那就只能選擇內部類實現多重繼承:
// With concrete or abstract classes, inner // classes are the only way to produce the effect // of "multiple implementation inheritance." package innerclasses; 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()); } }
閉包(closure)是一個可調用的對象,它記錄了一些信息,這些信息來自於建立它的做用域。能夠看出,內部類是面向對象的閉包。
經過內部類提供閉包的功能是優良的解決方案, 它比指針更靈活、更安全:
// Using inner class 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
*/
應用程序框架(application framework)就是被設計用以解決某類特定問題的一個類或一組類。
要運用某個應用程序框架,一般是繼承一個類或多個類,並覆蓋某些方法。
控制框架(control framework)是一類特殊的應用程序框架,它用來解決響應事件的需求。主要用來響應事件的系統被稱爲事件驅動系統。
下面的例子包含了某些實現:
// 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(); }
下面的文件包含了一個用來管理並觸發事件的實際控制框架:
// The reusable framework for control system. 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); } } } } }
使用內部類,能夠在單一的框架裏面產生對同一個基類Event的多種導出版本。對於溫室系統的每一種行爲,都繼承一個新的Event內部類,並在要實現的action()中編寫控制代碼。
溫室控制系統:
// 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";} } }
下面的類經過建立一個GreenhouseControls對象,並添加各類不一樣的Event對象來配置該系統:
// 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),m 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 */
內部類的繼承
由於內部類的構造器必須鏈接到指向外圍類對象的引用,因此在繼承內部類的時候,事情會變得複雜。問題在於,那個指向外圍類對象的「祕密的」引用必須被初始化,而在導出類中再也不存在可鏈接的默認對象。
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 i1 = new InheritInner(wi); } }
InheritInner只能繼承自內部類,且必須在構造器內使用以下語法:enclosingClassReference.super();,這樣才能提供必要的引用,而後程序才能編譯經過。
「覆蓋」內部類就好像它是外圍類的一個方法,其實並不起做用。
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() */
這個例子說明了,當繼承了某個外圍類的時候,內部類並無發生什麼特別神奇的變化。這兩個內部類是徹底獨立的兩個實體,各自在本身的命名空間內。
固然,明確地繼承某個內部類也是能夠的:
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() */
局部內部類(在方法體裏面建立)
1. 局部內部類不能有訪問說明符
2. 能夠訪問當前代碼塊內的全部常量
3. 能夠訪問外圍類的全部成員
局部內類與匿名內部類建立的比較:
// 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 */
每一個類都會產生一個.class文件,其中包含了如何建立該類型的對象的所有信息(此信息中產生一個"meta-class",叫作class對象)。
內部類也必須產生一個.class文件以包含它們的信息。它們的命名規則是:外圍類的名字加上「$」,再加上內部類的名字。
例如,LocalInnerClass.java生成的.class文件包括:
Counter.class
LocalInnerClass$1.class
LocalInnerClass$1LocalCounte.class
LocalInnerClass$1LocalCounter.class
LocalInnerClass.class
以上中,若是內部類是匿名的,編譯器會簡單地產生一個數字做爲其標示符。