
7.2 访问外部类

扫码看视频
当我们创建一个内部类的对象时,它就拥有了与外部类对象之间的一种联系,这是通过一个特殊的this引用形成的,使得内部类对象可以随意地访问外部类中的所有成员。图7-1展示了这一过程。

图7-1 内部类对象可以访问外部类的成员的原理图
注意图中的Outer.this,在内部类中访问外部类的成员,可以通过“外部类名称.this”的形式来获得外部类对象的引用。代码7.1中Inner类的print方法内部访问外部类的实例变量index,也可以通过Outer.this.index的方式来访问。当然,在一般情况下,不需要这么麻烦,因为这种关系是隐含建立的,所以不需要显式地去使用“外部类名称.this”的方式来访问外部类成员。不过当内部类中的成员和外部类中的成员重名时,或者内部类有多层嵌套时,就需要使用“外部类名称.this”这种方式来显式地指定访问的是哪一个成员。我们看代码7.2。


在Outer类中定义了一个内部类Middle,在Middle类中还有一个内部类Inner,这三个类都有一个同名的实例变量index,在Inner类的print方法中,为了明确访问的是哪一个index,我们需要使用“外部类名称.this”的形式来指定引用的变量。
接下来我们想看看代码7.2运行的结果,这时就牵涉如何创建内部类的对象了。代码7.1的Inner类对象是在外部类Outer的print方法中创建的,如果我们想在外部类之外直接创建内部类的对象是否可以呢?修改代码7.2,在Test类的main方法中添加直接创建Inner类对象的代码,如代码7.3所示。

如果在外部类之外访问内部类,那么可以把外部类名称看成是名称空间,采用“外部类名称.内部类名称”的形式来引用内部类,如果内部类有多层嵌套,那么一一加上其外围类的名称即可,如上述代码7.3所示。
编译Outer.java,编译器提示如图7-2所示的错误。

图7-2 创建内部类的对象报错
这个错误是什么意思呢?前面刚说了,内部类之所以可以随意访问外部类的成员,其原因在于内部类对象被创建时,会自动建立与外部类对象的一种联系,这种联系首先需要外部类的对象存在才可以建立。现在我们直接创建内部类对象,根本不存在外部类对象,怎么去建立这种联系呢?自然要报错了。
若要创建内部类对象,则需要先创建外部类对象,我们修改代码7.3,先创建外部类对象,如代码7.4所示。

有没有觉得这段代码很奇怪,其分别创建了三个类的对象,难道这三个类的对象就自动建立联系了吗?答案是否定的,编译上述代码,依然会出现如图7-2所示的错误。
由于内部类对象需要与外部类对象建立关联,因此内部类对象必须通过外部类对象来创建,这样才能建立联系,而不是简单地new。要建立这种联系,需要使用“外部类对象.new 内部类名称([构造方法参数列表])”这种特殊的语法形式来创建内部类对象。
修改代码7.4,如代码7.5所示。

在.new之后,不需要再添加外部类的名称了,因为前面已经有类型的完整名称了。
编译Outer.java,一切正常,执行Test类,输出结果如下:

可以看到内部类对象的创建是比较麻烦的,不过读者不用担心,在实际开发中,如果需要在外部用到内部类对象,则一般由外部类给出一个获取内部类对象的方法。这不仅可以简化内部类对象的创建,还可以对外屏蔽内部类对象的创建细节。
修改代码7.5,如代码7.6所示。

在Middle类中给出了获取Inner类对象的方法,在Outer类中给出了获取Middle类对象的方法,于是在main方法中的代码就变得熟悉而亲切了。