链接外部类
当某个外围类的对象创建了一个内部类对象时,此内部类对象必定会秘密地捕获一个指向那个外围类对象的引用。然后,在你访问此外围类的成员时,就是用那个引用来选择外围类的成员。幸运的是,编译器会帮你处理所有的细节,但你现在可以看到:内部类的对象只能在与其外围类的对象相关联的情况下才能被创建(就像你应该看到的,内部类是非 static 类时)。构建内部类对象时,需要一个指向其外围类对象的引用,如果编译器访问不到这个引用就会报错。不过绝大多数时候这都无需程序员操心。
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();
}
}
}
>输出结果:
0 1 2 3 4 5 6 7 8 9
使用 .this 和 .new
public class DotThis {
void f() { System.out.println("DotThis.f()"); }
public class Inner {
public DotThis outer() {
// error: cannot find symbol
return DotThis.this;
return DotThis.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();
}
}
>输出结果:
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();
}
}
要想直接创建内部类的对象,你不能按照你想象的方式,去引用外部类的名字 DotNew,而是必须使用外部类的对象来创建该内部类对象,就像在上面的程序中所看到的那样。这也解决了内部类名字作用域的问题,因此你不必声明(实际上你不能声明)dn.new 或 DotNew.Inner。
在拥有外部类对象之前是不可能创建内部类对象的。这是因为内部类对象会暗暗地连接到建它的外部类对象上。但是,如果你创建的是嵌套类(静态内部类),那么它就不需要对外部类对象的引用:
public class InnerStatic {
static class Inner {
public Inner() {
System.out.println("inner()");
}
}
public static void main(String[] args) {
Inner i = new Inner();
}
}
>输出结果:
inner()
内部类与向上转型
public interface Destination { String readLabel(); }
public interface Contents { int value(); }
class Parcel4 {
private class PContents implements Contents {
@Override
public int value() { return -1; }
}
protected final class PDestination implements Destination {
@Override
public String readLabel() { return "Tasmania"; }
}
public Contents contents() {
return new PContents();
}
public Destination destination(String s) {
return new PDestination(s);
}
}
public class TestParcel {
public static void main(String[] args) {
Parcel4 p = new Parcel4();
Contents c = p.contents();
Destination d = p.destination();
// Illegal -- can't access private class:
// Parcel4.PContents pc = p.new PContents();
}
}
在 Parcel4 中,内部类 PContents 是 private,所以除了 Parcel4,没有人能访问它。普通(非内部)类的访问权限不能被设为 private 或者 protected;他们只能设置为 public 或 package 访问权限。
PDestination 是 protected,所以只有 Parcel4 及其子类、还有与 Parcel4 同一个包中的类(因为 protected 也给予了包访问权)能访问 PDestination,其他类都不能访问 PDestination,这意味着,如果客户端程序员想了解或访问这些成员,那是要受到限制的。实际上,甚至不能向下转型成 private 内部类(或 protected 内部类,除非是继承自它的子类),因为不能访问其名字,就像在 TestParcel 类中看到的那样。
private 内部类给类的设计者提供了一种途径,通过这种方式可以完全阻止任何依赖于类型的编码,并且完全隐藏了实现的细节。此外,从客户端程序员的角度来看,由于不能访问任何新增加的、原本不属于公共接口的方法,所以扩展接口是没有价值的。这也给 Java 编译器提供了生成高效代码的机会。
class Base {
private class Pri_Inner {
public Pri_Inner() {
System.out.println("private inner()");
}
}
protected class Pro_Inner {
public Pro_Inner() {
System.out.println("protected inner()");
}
}
public class Pub_Inner {
public Pub_Inner() {
System.out.println("public inner()");
}
}
final class Final_Inner {
public Final_Inner() {
System.out.println("final innner()");
}
}
}
public class InnerStatic extends Base{
public static void main(String[] args) {
Base b = new Base();
Base.Pub_Inner pub = b.new Pub_Inner();
Base.Pro_Inner pro = b.new Pro_Inner();
//-error
// Base.Pri_Inner pri = b.new Pri_Inner();
}
}
内部类方法和作用域
public class Parcel5 {
public Destination destination(String s) {
final class PDestination implements Destination {
private String label;
@Override
public String readLabel() { return "Tasmania"; }
}
return new PDestination(s);
}
public static void main(String[] args) {
Parcel5 p = new Parcel5();
Destination d = p.destination();
}
}
PDestination 类是 destination() 方法的一部分,而不是 Parcel5 的一部分。所以,在 destination() 之外不能访问 PDestination,注意出现在 return 语句中的向上转型-返回的是 Destination 的引用,它是 PDestination 的基类。当然,在 destination() 中定义了内部类 PDestination,并不意味着一旦 dest() 方法执行完毕,PDestination 就不可用了。