从零学java(13)——内部类(1)
最近一个多月都在做一个Android应用(比赛),昨天下午提交了代码和文档,昨晚完成了视频的制作(第一次做视频)。后面还要做一些演示和答辩,不过这些都不涉及编码,终于松了一口气。
继续写java这个系列,内部类看了两遍,在此权当复习一下。由于内部类接触不多,这里要举不少书中代码。
内部类顾名思义,就是在一个类中定义另一个类。至少从表面上看来,它有一些有用的地方,比如对代码的整合,另外,可以通过内部类中的元素的可视性来实现某些目的。那先举一个简单的内部类的例子:
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; }
}
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");
}
}
当生成一个内部类的对象时,此对象与制造它的外围对象之间就有了一种联系,所以它能够访问其外围对象的所有成员,而不需要任何特殊条件。实际上,外围类创建了一个内部对象时,此内部对象必定会秘密地捕获一个指向那个外围类的对象的引用,不过不必担心,这些都是有编译器来处理的。
如果你需要生产对外部对象的引用,可以使用外部类的名字后面紧跟圆点和this:
public class DotThis {
void f() { System.out.println("DotThis.f()"); }
public class Inner {
public DotThis outer() {
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();
}
}
如果你想告知某些其他对象,去创建某个内部类的对象,则:
public class DotNew {
public class Inner {}
public static void main(String[] args) {
DotNew dn = new DotNew();
DotNew.Inner dni = dn.new Inner();
dti.outer().f();
}
}
你不必也不能声明为dn.new DotNew.Inner()
。
还有一种称为嵌套类的方式,即静态内部类,它不需要对外部类对象的引用(记得之前的那些静态类么,不需要实例化就可以使用)。通常,不能在接口内部放置代码,但嵌套类可以作为接口的一部分。
private内部类给类的设计者提供了一种途径,通过这种方式可以完全阻止任何依赖于类型的编码,并且完全隐藏了实现的细节。此外,从客户端程序员的角度来看,由于不能访问任何新增加的,原本不属于公共接口的方法,所以扩展接口是没有价值的。书中的这段话比较抽象,我们来看看下面这个例子:
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();
}
}
我们发现,PContents是私有类型的类,外部不能够访问,而内部类PDestination是protected型,也只有Parcel4和其子类才能访问。这样处理,使得客户端程序员来了解或访问这些成员,是要受限的。
接下来要说一个重要的概念——匿名内部类。如果你在阅读别人代码的过程中遇到了一些奇怪的用法(Android中有很多),别着急,那非常可能是匿名内部类。看看下面的例子:
public class Parcel7 {
public Contents contents() {
return new Contents() {
private int i = 11;
public int value() { return i; }
}; // 注意分号
}
public static void main(String[] args) {
Parcel7 p = new Parcel7();
Contents c = p.contents();
}
}
初看,似乎定义了一个叫做contents的方法,它返回一个Contents对象。但却在返回后插入了一个类的定义(注意分号)。实际上,是创建一个继承自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 p = new Parcel7b();
Contents c = p.contents();
}
}
实际上,匿名类将MyContents类的定义,与一个返回该类对象的方法合并到了一起。这时,MyContents由于是继承Contents类(接口),在匿名类中,直接向上转型为Contents的引用。
如果匿名类想要使用一个在其外部定义的对象,那么参数应该是final型:
public class Parcel9 {
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");
}
}
如果希望为匿名内部类创建一个构造器的效果,则:
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();
}
}
这里不要求变量i一定是final的,因为i被传递给匿名类的基类的构造器,而并不会被匿名类内部被直接使用。
blog comments powered by Disqus