將一個類的定義放在另一類的定義內部,這就是內部類
一、 創建內部類
把內部類的定義置於外圍類的裏面:
//: 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