第十章 内部类
目录
内部类允许把一些逻辑相关的类组织在一起,并控制位于内部的类的可见性
内部类与组合是完全不同的概念
内部类是一种代码隐藏机制:将类置于其他类的内部;了解外围类,并能与之通信
10.1 创建内部类
把类的定义置于外围类的里面:
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("Beijing");
}
}
/* Output:
* Beijing
*/
典型应用:外部类有一个方法,返回指向内部类的引用:
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 = new Contents();
Destination d = new Destination(dest);
System.out.println(d.readLabel());
}
public static void main(String[] args) {
Parcel2 p = new Parcel2();
p.ship("Beijing");
Parcel2 q = new Parcel2();
// Defining references to inner classes:
Parcel2.Contents c = q.contents();
Parcel2.Destination d = q.to("Nanjing");
}
}
/* Output:
* Beijing
*/
如果想从外部类的非静态方法之外的任何位置创建某个内部类的对象,name必须具体的指明这个对象的类型:OuterClassName.InnerClassName
10.2 链接到外部类
当生成一个内部类的对象时,此对象与制造它的外围对象(enclosing object)之间就有了一种联系,能访问其外围对象的所有成员,而不需要任何特殊条件
内部类还拥有其外围类的所有元素的访问权
“迭代器”设计模式的一个例子:
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;
@Override
public boolean end() {
return i == items.length;
}
@Override
public Object current() {
return items[i];
}
@Override
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
*/
SequenceSelector可以使用外部类Sequence的private成员items
当某个外围类的对象创建了一个内部类的对象时,此内部类对象必定会秘密地捕获一个指向那个外部类对象的引用。然后,在访问外部类的成员时,就是用那个引用来选择外围类的成员。
在内部类是非static类似,内部类的对象只能在与其外围类的对象相关联的情况下才能被创建:构建内部类对象时需要一个指向其外围类对象的引用,否则编译器会报错
10.3 使用.this与.new
如果你需要生成对外部类对象的引用,可以使用外部类的名字后面紧跟.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语法
public class DotNew {
public class Inner {}
public static void main(String[] args) {
DotNew dn = new DotNew();
DotNew.Inner dni = dn.new Inner();
}
}
要想直接创建内部类的对象,必须使用外部类的对象来创建该内部类对象
在拥有外部类对象之前是不可能创建内部类对象的(因为内部类对象会暗暗地连接到创建它的外部类对象上)
如果创建的是嵌套类(静态内部类),就不需要对外部类对象的引用
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("Beijing");
}
}
10.4 内部类与向上转型
当将内部类向上转型为其基类,尤其是转型为一个接口的时候,内部类就有了用武之地。(从实现了某个接口的对象得到对此接口的引用,与向上转型为这个对象的基类,实质上效果是一样的)。这是因为此内部类——某个接口的实现——能够完全不可见,并且不可用。所得到的只是指向基类或接口的引用,所以能够很方便地隐藏实现细节。
public interface Destination {
String readLabel();
}
public interface Contents {
int value();
}
public class Parcel4 {
private class PContents implements Contents {
private int i = 11;
@Override
public int value() {
return i;
}
}
protected class PDestination implements Destination {
private String label;
private PDestination(String label) {
this.label = label;
}
@Override
public String readLabel() {
return label;
}
}
public Contents contents() {
return new PContents();
}
public Destination destination(String label) {
return new PDestination(label);
}
}
public class TestParcel {
public static void main(String[] args) {
Parcel4 p = new Parcel4();
Contents c = p.contents();
Destination d = p.destination("Beijing");
// Illegal -- can't access private class
//! Parcel4.PContents pc = p.new PCcontents()
}
}
Contents Destination表示客户端程序员可用的接口
当取得了一个指向基类或接口的引用时,甚至可能无法找出其确切类型
内部类PContents是private,所以除了Parcel4没有人能访问它
PDestination是protected,所以只有Parcel4及其子类,与Parcel4在同一包中的类能访问
这意味着,如果客户端程序员想了解或访问这些成员是要受限制的
实际上,甚至不能向下转型成private内部类(或protected内部类,除非是继承自它的子类),因为不能访问名字
private内部类:
完全阻止任何依赖于类型的编码,并完全隐藏了实现细节
从客户端程序员角度:扩展接口是没有价值的(不能访问新增加的不属于公共接口的方法)
给Java编译器提供了生成更高效代码的机会
10.5 在方法和作用域内的内部类
局部内部类:在方法的作用域内
public class Parcel5 {
public Destination destination(String s) {
class PDestination implements Destination {
private String label;
public PDestination(String label) {
this.label = label;
}
@Override
public String readLabel() {
return label;
}
}
return new PDestination(s);
}
public static void main(String[] args) {
Parcel5 p = new Parcel5();
Destination d = p.destination("Beijing");
}
}
PDestination是destination()的一部分,destination()外不能访问
在destination()中定义了内部类PDestination,不意味着一旦destination()方法执行完,PDestination就不可用了
可以在同一个子目录下的任意类中使用类标识符PDestination,这并不会有命名冲突
例:在任意作用域中嵌入一个内部类
public class Parcel6 {
private void internalTracking(boolean b) {
if(b) {
class TrackingSlip {
private String id;
TrackingSlip(String id) {
this.id = id;
}
public String getId() {
return id;
}
}
TrackingSlip ts = new TrackingSlip("ts");
String s = ts.getId();
}
// 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();
}
}
TrackingShip类被嵌入在if语句的作用域中,并不是说该类的创建时有条件的,它其实与别的类一起编译过了。然而,在定义TrackingShip的作用域之外,它是不可用的;除此之外,它和普通的类一样
10.6 匿名内部类
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();
}
}
是下述形式的简化形式:
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 p7b = new Parcel7b();
Contents contents = p7b.contents();
}
}
如果基类需要一个有参数的构造器:
public class Wrapping {
private int i;
public Wrapping(int i) {
this.i = i;
}
public int value() {
return i;
}
}
public class Parcel8 {
public Wrapping wrapping(int x) {
return new Wrapping(x) { // Pass constructor argument.
public int value() {
return super.value();
}
};
}
public static void main(String[] args) {
Parcel8 p = new Parcel8();
Wrapping w = p.wrapping(8);
}
}
只需简单地传递合适的参数给基类的构造器
Wrapping只是一个具有具体实现的普通类,但还是被其到处类当做公共“接口”使用
在匿名类部内中定义字段时,还能够对其执行初始化操作
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;
@Override
public String readLabel() {
return label;
}
};
}
public static void main(String[] args) {
Parcel9 p = new Parcel9();
Destination destination = p.destination("Beijing");
}
}
如果定义一个匿名内部类,并且希望它使用一个在其外部定义的对象,那么编译器会要求其参数引用时final的(否则会得到一个编译时报错)
在匿名类内部不能有命名构造器(因为没有名字),但通过实例初始化就能够达到为匿名内部类创建一个构造器的效果
abstract class Base {
public Base(int i) {
System.out.println("Base constructor, i = " + i);
}
public abstract void f();
}
public class AnonymousConstructor {
public static Base getBase(int i) {
return new Base(i) {
{
System.out.println("Inside instance initializer");
}
@Override
public void f() {
System.out.println("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的,因为被传递给匿名类的基类的构造器,并不会在匿名类内部直接被使用
例:带实例初始化
public class Parcel10 {
public Destination destination(final String dest, final float price) {
return new Destination() {
private int cost;
{
cost = Math.round(price);
if(cost > 100) {
System.out.println("Over budget!");
}
}
private String label = dest;
@Override
public String readLabel() {
return label;
}
};
}
public static void main(String[] args) {
Parcel10 p = new Parcel10();
Destination d = p.destination("Beijing", 101.395F);
}
}
/* Output:
* Over budget!
*/
对于匿名类而言,实例初始化的实际效果就是构造器
受到限制:不能重载实例初始化方法,仅有一个这样的构造器
匿名内部类与正规的继承相比有些受限:
匿名内部类既可以扩展类,也可以实现接口,但不能两者兼备
如果是实现类,只能实现一个接口
10.6.1 再访工厂方法
public interface Service {
void method1();
void method2();
}
public interface ServiceFactory {
Service getService();
}
class Implementation1 implements Service {
private Implementation1() {
}
@Override
public void method1() {
System.out.println("Implementation1 method1()");
}
@Override
public void method2() {
System.out.println("Implementation1 method2()");
}
public static ServiceFactory factory = new ServiceFactory() {
@Override
public Service getService() {
return new Implementation1();
}
};
}
class Implementation2 implements Service {
private Implementation2() {
}
@Override
public void method1() {
System.out.println("Implementation2 method1()");
}
@Override
public void method2() {
System.out.println("Implementation2 method2()");
}
public static ServiceFactory factory = new ServiceFactory() {
@Override
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()
*/
用于Implementation1 Implementation2的构造器都可以是private的,并且没有任何必要去创建作为工厂的具名类。另外,经常只需要一个单一的工厂对象,所以在本例中它被创建为Service实现中的一个static域。
建议:
优先使用类而不是接口
如果你的设计中需要某个接口,你必须了解它
否则,不到迫不得已,不要将其放进你的设计中
10.7 嵌套类
嵌套类:如果不需要内部类对象与其外围对象之间有联系,可以将内部类声明为static
- 要创建嵌套类的对象并不需要其外围类的对象
- 不能吃嵌套类的对象中访问非静态的外围类对象
普通内部类的字段与方法,只能放在类的外部层次上,所以普通的内部类不能用static数据和static字段,也不能包含嵌套类;但嵌套类可以包含
10.7.1 接口内部的类
嵌套类可以作为接口的一部分,自动是public static的,甚至可以在内部类中实现接口
public interface ClassInterface {
void howdy();
class Test implements ClassInterface {
@Override
public void howdy() {
System.out.println("Howdy!");
}
public static void main(String[] args) {
new Test().howdy();
}
}
}
可以创建某些公共代码,使得他们可以被某个接口的所有不同实现共用
10.7.2 从多层嵌套类中访问外部类的成员
一个内部类被嵌套多少层并不重要——它能透明地访问所有它所嵌入的外围类的所有成员
class MNA {
private void f() {}
public class A {
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 为什么需要内部类
一般来说,内部类继承自某个类或实现某个接口,内部类的代码操作创建它的外围类的对象。所有可以认为内部类提供了某种进入外围类的窗口
如果只需要一个对接口的引用,通过外围类实现那个接口
外围类实现接口不是总能享用到接口带来的方便,有时需要用到接口的实现
每个内部类都能独自的继承一个(接口的)实现,所以无论外围类是否已经继承了某个(接口的)实现,对于内部类都没有影响
内部类有效地实现了“多重继承”,允许继承多个非接口类型(类或抽象类)
例:在一个类中以某种方式实现两个接口
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());
}
}
使用内部类还可以获得其他一些特性:
- 内部类可以有多个实例,每个实例有自己的状态信息,并与外围类对象的信息相互独立
- 在单个外围类中,可以让多个内部类以不同方式实现同一接口或继承同一类
- 创建内部类对象的时刻并不依赖于外围类对象的创建
- 内部类并没有令人迷惑的“is-a”关系;它是一个独立的实体
举例:如果Sequence.java不使用内部类,就必须声明“Sequence是一个Selector”,对于某个特定的Sequence只能有一个Selector。然而使用内部类很容易就能拥有另一个方法reverseSelector(),用它来生成一个反向遍历序列的Selector。只有内部类有这种灵活性。
10.8.1 闭包与回调
闭包(closure)是一个可调用的对象,它记录了一些信息,这些信息来自于创建它的作用域
内部类是面向对象的闭包(不仅包含外围类对象(创建内部类的作用域)的信息,还自动拥有一个指向此外围类对象的引用,在此作用域内,内部类有权操作所有的成员,包括private)
回调(callback):通过回调,对象能够携带一些信息,这些信息运行它在稍后的某个时刻调用初始的对象
通过内部类提供闭包的功能是优良的解决方案,它比指针更灵活、安全
interface Incrementable {
void increment();
}
// Very simple to just implement the interface
class Callee1 implements Incrementable {
private int i = 0;
@Override
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 {
@Override
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 callBackReference) {
this.callBackReference = callBackReference;
}
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
*/
Callee2继承了MyIncrement,就不能为了Incrementable的用途而覆盖increment()方法,于是只能使用内部类独立地实现Incrementable。
当创建了一个内部类时,并没有在外围类的接口中添加东西,也没有修改外围类的接口。
在Callee2中除了getCallBackReference()以外,其他成员都是private的。要想建立与外部世界的任何链接,interface Incrementable都是必须的。在这里可以看到,interface是如何允许接口与接口的实现是完全独立的。
内部类Closure实现了Incrementable,以提供一个返回Callee2的“钩子”(hook)——而且是一个安全的钩子。无论谁获得此Incrementable的引用,都只能调用increment()(不像指针允许做很多事)
Caller的构造器许杨一个Incrementable的引用作为参数(虽然可以在任意时刻捕获回调引用),然后在以后的某个时刻,Caller对象可以使用此引用回调callee类
回调的价值在于灵活性——可以在运行时动态地决定需要调用什么方法(例:GUI)
10.8.2 内部类与控制框架
控制框架(control framework)
应用程序框架(application framework):被设计用以解决某类特定问题的一个类或一组类。
变化的:可覆盖的方法 不变的:模板方法
事件驱动系统 e.g.GUI
内部类允许:
- 控制框架的完整实现是由单个的类创建的,从而使得实现的细节被封装了起来。内部类用来表示解决问题说必需的各种不同的action()
- 内部类能够很容易地访问外部类的任意成员,所以可以避免这种实现变得笨拙。
例:控制温室的运作
public abstract class Event {
private long eventTime;
protected final long delayTime;
public Event(long delayTime) {
this.delayTime = delayTime;
}
public void start() { // allows restarting
eventTime = System.nanoTime() + delayTime;
}
public boolean ready() {
return System.nanoTime() >= eventTime;
}
public abstract void action();
}
public class Controller {
private List<Event> eventList = new ArrayList<>();
public void addEvent(Event e) {
eventList.add(e);
}
public void run() {
while(eventList.size() > 0) {
// make a copy so you're not modifying the list
// while you're selectiong the elements in it:
for(Event e : eventList) {
if(e.ready()) {
System.out.println(e);
e.action();
eventList.remove(e);
}
}
}
}
}
public class GreenHouseControls extends Controller {
// 控制灯光的开关
private boolean light = false;
public class LightOn extends Event {
public LightOn(long delayTime) {
super(delayTime);
}
@Override
public void action() {
// put hardware control code here to
// physically turn on the light.
light = true;
}
@Override
public String toString() {
return "light is on";
}
}
public class LightOff extends Event {
public LightOff(long delayTime) {
super(delayTime);
}
@Override
public void action() {
// put hardware control code here to
// physically turn off the light.
light = false;
}
@Override
public String toString() {
return "light is off";
}
}
// 控制水的开关
private boolean water = false;
public class WaterOn extends Event {
public WaterOn(long delayTime) {
super(delayTime);
}
@Override
public void action() {
// put hardware control code here to
// physically turn on the water.
water = true;
}
@Override
public String toString() {
return "water is on";
}
}
public class WaterOnOff extends Event {
public WaterOnOff(long delayTime) {
super(delayTime);
}
@Override
public void action() {
// put hardware control code here to
// physically turn off the water.
water = false;
}
@Override
public String toString() {
return "water is off";
}
}
// 控制温度调节器的开关
private String thermostat = "Day";
public class ThermostatNight extends Event {
public ThermostatNight(long delayTime) {
super(delayTime);
}
@Override
public void action() {
// put hardware control code here
thermostat = "night";
}
@Override
public String toString() {
return "Thermostat on night setting";
}
}
public class ThermostatDay extends Event {
public ThermostatDay(long delayTime) {
super(delayTime);
}
@Override
public void action() {
// put hardware control code here
thermostat = "day";
}
@Override
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);
}
@Override
public void action() {
addEvent(new Bell(delayTime));
}
@Override
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);
}
}
@Override
public void action() {
for(Event e : eventList) {
e.start(); // rerun each event
addEvent(e);
}
start(); // rerun this event
addEvent(this);
}
@Override
public String toString() {
return "restarting system";
}
}
public static class Terminate extends Event {
public Terminate(long delayTime) {
super(delayTime);
}
@Override
public void action() {
System.exit(0);
}
@Override
public String toString() {
return "terminating";
}
}
}
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 WaterOnOff(800),
gc.new ThermostatDay(1000)
};
gc.addEvent(gc.new Restart(2000, eventList));
if(args.length == 1) {
gc.addEvent(
new GreenHouseControls.Terminate(
new Integer(args[0])
)
);
}
gc.run();
}
}
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);
}
}
在继承内部类的时候,指向外围类对象的“秘密的”引用必须被初始化
构造器:不能默认,不能只传递一个指向外围类对象的引用
enclosingClassReference.super();
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 {
protected 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 Egg()");
}
public void insertYolk(Yolk yy) {
y = yy;
}
public void g() {
y.f();
}
}
public class BigEgg2 extends Egg2 {
protected 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 Egg()
* Egg2.Yolk()
* BigEgg2.Yolk()
* BigEgg2.Yolk.f()
*/
BigEgg2.Yolk通过extends Egg2.Yolk明确继承了此内部类,并且覆盖了其中的方法。insertYork(0方法运行BigEgg2将它自己的Yolk对象向上转型为Egg2中的引用y。所以当g()调用y.f()时,覆盖后的新版的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()");
}
@Override
public int next() {
System.out.print(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()");
}
@Override
public int next() {
System.out.print(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 内部类标识符
每个类都会产生一个.class文件,其中包含了如何创建该类型的对象的全部信息(此信息产生一个“meta-class”,叫做Class对象)
内部类也必须生成一个.class文件以包含它们的Class对象信息
命名:外围类的名字,加上“$”,再加上内部类的名字
如果内部类是匿名的,编译器会简单地产生一个数字作为标识符
如果内部类是嵌套在别的内部类之中,只需直接将它们的名字加载其外围类标识符与“$”的后面