Thinking in Java from Chapter 10
From Thinking in Java 4th Edition
内部类
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 | 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 */ |
更典型的情况是,外部类将有一个方法,该方法返回一个指向内部类的引用
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 | 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 */ |
当生成一个内部类的对象时,此对象与制造它的外围对象之间就有了联系,它能访问其外围对象的所有成员。内部类拥有其外围类的所有元素的访问权:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 | 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:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 | 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语法:
1 2 3 4 5 6 7 8 | public class DotNew { public class Inner {} public static void main(String[] args){ DotNew dn = new DotNew(); DotNew.Inner dni = dn. new Inner(); } } |
要直接创建内部类的对象,你不能按照想象的方式,去引用外部类的名字DotNew,而必须使用外部类的对象来创建该内部类对象.
在拥有外部类对象之前是不可能创建内部类对象的。这是因为内部类对象会暗暗地连接到创建它的外部类对象上。
但如果你创建的是嵌套类(静态内部类),那就不需要对外部类对象的引用:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 | 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" ); } } |
内部类与向上转型
示例接口:
1 2 3 4 5 6 7 | public interface Destination { String readLabel(); } public interface Contents { int value(); } |
当取得一个指向基类或接口的引用时,甚至可能无法找出它的确切类型:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 | 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(); } } |
可以在一个方法里或者在任意的作用域内定义内部类。
在方法的作用域内创建一个完整的内,称作局部内部类:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 | 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命名,这并不会有名字冲突。
在任意的作用域内嵌入一个内部类:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 | 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的作用域之外,它是不可用的
匿名内部类
1 2 3 4 5 6 7 8 9 10 11 12 13 | 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的引用,上述匿名内部类的语法是下述形式的简化形式:
1 2 3 4 5 6 7 8 9 10 11 12 13 | 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,下面展示,如果你的基类需要一个有参数的构造器应该怎么办:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | public class Wrapping {<br> private int i;<br> public Wrapping( int x) { i = x;}<br> public int value() { return i;}<br>}<br><br> 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的:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | 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" ); } } |
如果想做一些类似构造器的行为,在匿名类中不可能有命名构造器(因为它根本没有名字!),但通过实例初始化,就能够达到匿名内部类创建一个构造器的效果。
目标效果:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 | 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” 形式:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 | 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! */ |
运用匿名内部类实现工厂方法:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 | 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类也可以经由同样的改进:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 | 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字段。但是嵌套类可以包含所有这些东西
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 | 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的,所以这并不违反接口的规则。甚至,可以在内部类中实现其外围的接口:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | //: innerclasses/ClassInInterface.java<br>// {main: ClassInInterface$Test}<br><br>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即可。)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 | //: 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:<br>f()<br>*/ |
这生成了一个独立的类TestBed$Tester。 可以使用这个类来做测试,但不必在发布的产品中包含它,在将产品打包前可以简单地删除TestBed$Tester.class文件。
一个内部类被嵌套多少层并不重要,它能透明地访问所有它所嵌入的外围类的所有成员:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 | 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. 使用内部类
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 | package innerclasses; interface A {} interface B {} <br> // 1. Use only one class class X implements A, B {} <br><br> // 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()); } } |
如果拥有的是抽象的类或具体的类,而不是接口, 那就只能选择内部类实现多重继承:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 | // 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)是一个可调用的对象,它记录了一些信息,这些信息来自于创建它的作用域。可以看出,内部类是面向对象的闭包。
通过内部类提供闭包的功能是优良的解决方案, 它比指针更灵活、更安全:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 | // 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:<br>Other operation<br>1<br>1<br>2<br>Other operation<br>2<br>Other operation<br>3<br>*/ |
应用程序框架(application framework)就是被设计用以解决某类特定问题的一个类或一组类。
要运用某个应用程序框架,通常是继承一个类或多个类,并覆盖某些方法。
控制框架(control framework)是一类特殊的应用程序框架,它用来解决响应事件的需求。主要用来响应事件的系统被称为事件驱动系统。
下面的例子包含了某些实现:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 | // 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(); } |
下面的文件包含了一个用来管理并触发事件的实际控制框架:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 | // 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()中编写控制代码。
温室控制系统:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 | // 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对象来配置该系统:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 | // 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 */ |
内部类的继承
因为内部类的构造器必须连接到指向外围类对象的引用,所以在继承内部类的时候,事情会变得复杂。问题在于,那个指向外围类对象的“秘密的”引用必须被初始化,而在导出类中不再存在可连接的默认对象。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | 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();,这样才能提供必要的引用,然后程序才能编译通过。
“覆盖”内部类就好像它是外围类的一个方法,其实并不起作用。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 | 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() */ |
这个例子说明了,当继承了某个外围类的时候,内部类并没有发生什么特别神奇的变化。这两个内部类是完全独立的两个实体,各自在自己的命名空间内。
当然,明确地继承某个内部类也是可以的:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 | 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. 可以访问外围类的所有成员
局部内类与匿名内部类创建的比较:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 | // 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
以上中,如果内部类是匿名的,编译器会简单地产生一个数字作为其标示符。
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· AI与.NET技术实操系列:向量存储与相似性搜索在 .NET 中的实现
· 基于Microsoft.Extensions.AI核心库实现RAG应用
· Linux系列:如何用heaptrack跟踪.NET程序的非托管内存泄露
· 开发者必知的日志记录最佳实践
· SQL Server 2025 AI相关能力初探
· 震惊!C++程序真的从main开始吗?99%的程序员都答错了
· winform 绘制太阳,地球,月球 运作规律
· 【硬核科普】Trae如何「偷看」你的代码?零基础破解AI编程运行原理
· 超详细:普通电脑也行Windows部署deepseek R1训练数据并当服务器共享给他人
· 上周热点回顾(3.3-3.9)