今天我们说内部类
keywords :inner-class 将一个类的定义放在另一个类的定义内部
有了她,我们可以把一些逻辑相关的类组织起来并放到一起,并控制位于内部类的可视性。起初我们完全会做出内部类是一种代码的隐藏机制的判断,但实际上内部类远非如此,她是如此的优雅,可以与外部类发生关系,进行通信。
inner-class 看上去是令人感到怪异的,但她的特性依然让人着迷,而且你必须花费更多的时间去设计以及实践性的去使用他。然而在大多数时间里面,对inner-class的需求并非显得那么的明显,但是一旦你掌握并可以灵活的使用她,我相信其中的益处也是显而易见的。那么,我们还在等什么呢?
现在,我们将拥有一个内部类:
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 readLabel;
}
}
public void ship(String dest){
Contents c = new Contents();
Destination d = new Destination();
System.out.println(d.readLabel());
}
public static void man(String[] args){
Parcel1 p =new Parcel1();
p.ship(“Tasmania”);
}
}//Use this code and into your compiler, see the result .
就这段代码而言,实际上通过嵌套,外部类Parcel1的对象p使用了其内部类Destination 中的readLabel方法 ,但是这样的用法没有什么能够让我们惊呼的。
现在,我们将尝试更为奇妙的东西,她将实现这样一个目的:out-class有一个方法,其将返回一个指向内部类的引用;
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();
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”);
}
}//Use this code and into your compiler, see the result.
正如我们所看到的,粗体字表明了需要在一个out-class的非static 方法之外的任意位置使用inner-class中的object的用法。
接下来我们来研究以下inner-class 和out-class是如何发生关系链接
现在,你是否还是这样认为,inner-class只是一种名字隐藏和组织代码的模式。你仍然没有感觉到她的奇妙之处。那么现在请注意下面这句话:当生成一个inner-class的objects时,此object与制造它的enclosing object便发生了关系。这种关系就体现在object可以access enclosing object 中的所有成员(attribute or field ,and functions),而这却不需要任何的代价。
看看这段有趣儿的代码:
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.println(selector.end() + “ ”);
selector.next();
}
}
}//Use this code and into your compiler, see the result.
Class Sequence 中固定大小的Object数组,是以类的形式包装起来的。只要还有空间即满足 next<items.length ,即可以调用add()方法以实现在序列的末尾处添加新的object。现在我们需要使用Sequence这个类中的对象,那么,不难想象到我们可以使用interface。请注意下面这句话,它有关于“迭代器”的设计模式:要获取Sequence中的每个对象,可以使用Selector接口。相应的,我们不难看出,所有实现了Selector接口的类看上去都是实现了这样一些功能的(function):end()方法可以检查序列是否到了末尾,current()方法可以访问当前对象,而next()功能则实现了移到序列的下一个对象。因为Selector是一个接口而非抽象类,所以很灵活的,当类SequenceSelector 实现了Selector接口时,它可以以自己的所喜欢的style去运转。再看看SequenceSelector这个类,他是个Private的类。也正是通过他,Selector接口中所描绘出的功能,才能够得以实现。在入口方法中,我们将Sequence这个包含有一个固定长度的数组的类实例化了,并给它传递了一个整数型的参数。这样,一个定长的数组也就诞生了。接着我们利用对象包装器,向里面添加一些String类型的对象。最后,使用Sequence类的对象sequence的selector方法,得到一个返回值为协变返回类型的对象,并将其赋值给Selector接口的实例化selector。现在你还认为inner-class技术不过如此吗,如果你还这么想,那么再仔细看一下实现了Selector接口的类SequenceSelector中的三个方法,他们都用到了Object,在这里其实是一个引用,其实它并不属于类SequenceSelector,而仅仅是enclosing class 中的一个私有的filed。这样看来inner-class已经拥有了enclosing class中的所有内容。这是多么令人不可思议啊,你可能会这样问,那么这是如何实现的呢?inner-class 会在适当的时候catch一个指向那个enclosing-class的引用。就这样,当你用inner-class的对象access那个enclosing-class的内容的之前,会调用那个引用,实际上那个神秘兮兮“引用先生”在访问enclosing-class的内容。当然,这一切都是托了compiler的福。
关于.this和.new
看到这两个熟悉的keywords,你一定有很多想说的,他们对我们来说是显得那么的熟悉,但同时也常常会令我们十分苦恼。
现在想要引用out-class的object,那么可以这样写:
out-classname.this 这样做的好处就是我们可以自动获得一个正确类型的对象,编译器会帮助我们做好审查工作,换回来的当然是运行时的零开销。
看看这个“.this”是如何apply的:
public class DoThis{
void() f(){
System.out.println(“DoThis.f()”);
}
public class Inner{
public DoThis outer(){
return DoThis.this;
}
}
public Inner inner(){
return new Inner();
}
public static void main(String[] args){
DoThis dt = new DoThis();
DoThis.Inner dti = dt.inner();
dti.outer().f();
}
}Use this code and into your compiler ,see the result
现在你明白了,如何生成对外部类对象的引用,那么你也许会问,如果想要让某些小家伙(enclosing-class's object)去创建在它们自己内部的某个inner-class's object ?
接下来我将展示,上面的问题时如何在代码中得以实现的
public class DotNew{
public class inner{
//nothing in this class.
}
public static void main(String[] args){
DotNew dn = new DotNew();
DotNew.Inner dni = dn.new inner();
}
}//Use this code and into your compiler ,see the result.
请记住,如果想要去创建一个inner-class's object,那么应该使用enclosing-class's object 去创建一个inner-class's object ,所以拥有内部来对象的前提是,你必须先创建一个外部类的对象。不过这也有例外,下面看嵌套类,她是一个静态的内部类,她不需要对外部类对象的引用。
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 d = p.new Contents();
Parcel3.Destination d = p.new Destination(“Tasmania”)
}
}Use this code and into your compiler ,see the result.
有关inner-class和up-casting
现在我们试着将内部类up-casting 为一个interface,将会发现这个内部类的(其一定是某个接口的实现),能够完全不可见,并且不可用。我们得到的仅仅是指向其parent-class或者interface的reference,所以细节的东西得以实现隐藏。