第十章 内部类(上)
可以将一个类的定义放在另一个类的定义内部,这就是内部类。
内部类是一种非常有用的特性,它允许你把一些逻辑相关的类组织在一起,并控制位于内部的类的可视性,但内部类与组合是完全不同的概念。
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; } } 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"); } }
当我们在ship()方法里使用内部类的时候与使用普通类没什么不同,实际的区别就只是内部类的名字嵌套在Parcel1中。
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("Tasmania"); Parcel2 q = new Parcel2(); Parcel2.Contents c = q.contents(); Parcel2.Destination d = q.to("Borneo"); } }
如果想从外部类的非静态方法之外的任意位置创建某个内部类的对象,那么必须像在main()方法中那样,具体指明这个对象的类型:OuterClassName.InnerClassName。
10.2 链接到外部类
当生成一个内部类的对象时,此对象与制造它的外围对象之间就有了一种联系,它能访问其外围对象的所有成员,而不需要任何特殊条件,此外内部类还拥有其外围类的所有元素的访问权:
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(); } } }
Sequence只是一个固定大小的Object数组,以类的形式包装了起来,可以调用add()在序列末增加新的Object,要获取Sequence中的每一个对象,可以使用Selector接口。注意方法end()、current()、next()都用到了objects,这是一个引用,它并不是SequenceSelector的一部分,而是外围类中的一个private字段,然而内部类可以访问其外围类的方法和字段,这带来了极大的方便。
10.3 使用.this和.new
如果你需要生成对外部类对象的引用,可以使用外部类的名字后面紧跟.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(); } }
有时候你可能想要告知某些其他对象去创建其某个内部类的对象,你必须在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,而是必须使用外部类的对象来创建该内部类对象,就像在上面看到的那样,这就解决了内部类名字作用域的问题。
在拥有外部类对象之前是不可能创造内部类对象的,这是因为内部类对象会暗暗连接到创建它的外部类对象上,但如果你创建的是嵌套类(静态内部类),那么它就不需要对外部类对象的引用。
下面我们把.new应用到Parcel:
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(); Parcel3.Contents c = p.new Contents(); Parcel3.Destination d = p.new Destination("Tasmania"); } }
10.4 内部类与向上转型
当将内部类向上转型为基类,尤其是转型为一个接口的时候,内部类就有了用武之地,这是因为内部类——某个接口的实现——能够完全不可见并且不可用,所得到的知识指向基类或接口的引用,所以能够很方便地隐藏实现细节。
当取得了一个指向基类或接口的引用时,甚至可能无法找出它确切的类型:
public 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"); } }
10.5 在方法和作用域内的内部类
通常如果读写的代码包含了内部类,那么他们都是平凡的内部类,简单而且容易理解,然而内部类的语法覆盖了大量其他的难以理解的技术,例如,可以在一个方法里面或者在任意作用域内定义内部类,这么做有两个理由:
1、实现了某类型的接口,于是可以创建并返回对其的引用
2、要解决一个复杂的问题,想创建一个类来辅助你的解决方案,但是又不希望这个类是公共可用的
第一个例子展示在方法的作用域内创建一个完整的类,这被称作局部内部类:
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"); } }
PDestination类是destination()方法的一部分,而不是Parcel5的一部分,所以在destination之外不能访问PDestination。你可以在同一子目录下的任意类对某个内部类使用类标识符PDestination,这并不会有命名冲突。
下面的例子展示如何在任意的作用域中嵌入一个内部类:
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(); } } public void track() { internalTracking(true); } public static void main(String[] args) { Parcel6 p = new Parcel6(); p.track(); } }
TrackingSlip类被嵌入在if语句的作用域内,这并不是说该类的创建时有条件的,它其实与别的类一起编译过了,然而在定义TrackingSlip的作用域之外它是不可以红的,除此之外,它与普通的其他类一样。
10.6 匿名内部类
下面的例子看起来有点奇怪:
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的匿名类的对象,通过new表达式返回的引用被自动向上转型为对Contents的引用。匿名内部类与正规的继承相比有些受限,因为匿名内部类既可以扩展类,也可以实现接口,但是不能二者兼备,而如果是实现接口也只能实现一个接口。
10.6.1 再访工厂方法
优化工厂方法,此处略。
优先使用类不是接口,如果你设计中需要某个接口,你必须了解他,否则不到万不得已不要将其放在你的设计中。
原文地址:https://www.cnblogs.com/parable/p/11488003.html