上一篇:多态性之抽象类和接口
为什么使用内部类:
每个内部类都可以独立地继承自一个(接口的)实现,所以无论外围类是否已经继承了一个(接口的)实现,对内部类都没有影响。
如果没有内部类提供的、可以继承多个具体的或抽象的类的能力,一些设计与编程问题就很难解决。从这个角度看,内部类使得多重继承的解决方案变得更加完整。接口解决了部分问题,而内部类有效地实现了”多重继承“。也就是说,内部类允许继承多个非接口类型(类或抽象类)。
定义在一个类内部的类被称为内部类。内部类拥有对封装类所有元素的访问权限,因为内部类的对象默认持有创建它的那个封装类的一个对象的句柄。
若想在除外部类非 static 方法内部之外的任何地方生成内部类的一个对象,必须将那个对象的类型设为“外部类名.内部类名”,而且创建内部类实例必须先有一个外部类实例。
public class School{
class Student{ //内部类
int name;
}
public Student make(){
return new Student();
}
public static void main(String[] args){
School sh = new School();
School.Student st = sh.make(); //生成内部类对象
}
}
内部类的特点:
.this和 .new:
如果需要生成对外部类对象的引用,可以使用外部类的名字后面紧跟原点+this。
如果想创建某个内部类对象,必须在new表达式中提供对其他外部类的引用,这需要.new语法,例如:
public static void main(String[] args){
Outer outer = new Outer();
Outer.Inner inner = outer.new Inner();
}
内部类和上溯造型:
当我们准备上溯造型到一个基础类(特别是到一个接口)的时候,内部类就开始发挥其关键作用。这是由于内部类随后可完全进入不可见或不可用状态——对任何人都将如此。所以我们可以非常方便地隐藏实施细节。我们得到的全部回报就是一个基础类或者接口的句柄,而且甚至有可能不知道准确的类型。
interface Destination { //接口
String readLabel();
}
public class Parcel3 {
protected class PDestination implements Destination { //内部类
public String readLabel() { System.out.println("readLabel"); }
}
public Destination dest() {
return new PDestination();
}
}
class Test {
public static void main(String[] args) {
Parcel3 p = new Parcel3();
Destination d = p.dest(); //上溯造型
}
}
定义在方法和作用域中的内部类:
上面的情况是使用内部类的经典情况,但内部类还有其他的用法:
采用方法或作用域中的内部类有两个原因:
下面这个例子是在方法的作用域中定义内部类:
interface Dec {
String readLabel();
}
public class Par {
public Dec dest() { //含有内部类的方法
class PDec implements Dec { //内部类继承接口
public String readLabel() { System.out.println("readLabel"); }
}
return new PDec(); //上溯造型
}
public static void main(String[] args) {
Par p = new Par();
Dec d = p.dest();
}
}
仔细分析上述代码,PDec类属于dest()的一部分而不是Par()的一部分,所以PDec不能在dest()的外部被访问。请注意在返回语句中发生的上溯造型——除了指向基础类Dec的一个句柄之外,没有任何东西超出dest()的边界之外。当然, 不能由于类PDec的名字置于 dest()内部,就认为在 dest()返回之后 PDestination 不是一个有效的对象。
任意定义域中的内部类:
if(b) {
class Move {
//...
}
Move m = new Move();
//...
}
在定义的范围外,这个内部类是不可见的,除此之外它和普通类没什么区别。
匿名内部类:
public class Parcel6 {
public Contents cont() {
return new Contents() { //匿名类
private int i = 11;
public int value() { return i; }
}; //必须用分号
}
//main()方法
}
上面代码包含一个匿名类--它没有名字。这种奇怪的语法要表达的意思是:“创建从 Contents 衍生出来的匿名类的一个对象”。由 new 表达式返回的 句柄会自动上溯造型成一个Contents 句柄。上述匿名类代码等同于:
class MyContents extends Contents {
private int i = 11;
public int value() { return i; }
}
return new MyContents();
由于没有名字,所以匿名类没有构造器,所以我们也不能直接生成一个匿名类的对象,但我们可以通过包含匿名类的方法来实现:
public class Par {
public Destination dest(final String dest) {
return new Destination() {
private String label = dest;
public String readLabel() { return label; }
};
}
public static void main(String[] args) {
Par p = new Par();
Destination d = p.dest("Tanzania");
}
}
分析上述代码,我们可以通过对dest()方法的调用来生成匿名类对象,而且该方法可以认为是匿名类的构建方法,当然它的功能是有限的:因为无法重载,所以“构建器”只能有一个。
从内部类(非static)继承:
由于内部类构建器必须同封装类对象的一个句柄联系到一起(见上面生成内部类对象的代码),所以从一个内部类继承的时候,情况会稍微变得有些复杂。这儿的问题是封装类的“秘密”句柄必须获得初始化,而且在衍生类中不再有一个默认的对象可以连接。解决这个问题的办法是采用一种特殊的语法,明确建立这种关联:
class WithInner {
class Inner {}
}
public class InheritInner extends WithInner.Inner { //继承内部类
//! InheritInner() {} // 不会通过编译,因为继承的内部类的封装类没有显式初始化
InheritInner(WithInner wi) {
wi.super(); //保证封装类得到初始化
}
public static void main(String[] args) {
WithInner wi = new WithInner();
InheritInner ii = new InheritInner(wi);
}
}
下一篇:多态性之嵌套类