如你所知,Java是一种类型严格的语言.除了用于基本类型的标准转换和自动升级,类型兼容也是被严格执行的.因此,一个类类型的引用变量通常不能引用一个其他类型的对象.例如,考虑下面的程序:
public class test2 { // @param args public static void main(String args[]) { X x = new X(10); X x2; Y y = new Y(5); x2 = x;//ok, both of same type x2 = y;//error not of same type } } class X { int a; X(int i){ a = i;} } class Y { int a; Y(int i){ a = i;} }
这里,尽管X类和Y类本质上是一样的,但是不能把一个Y对象赋值给一个X引用,因为他们的类型不同.总之,对象引用变量只能引用自己类型的对象.
然而,Java的严格类型却有一个例外,即可以把对超类派生子类对象的引用赋给一个超类的引用变量.换句话说,超类引用可以引用子类对象,如下所示:
public class test2 { // @param args public static void main(String args[]) { X x = new X(10); X x2; Y y = new Y(5); x2 = x;//ok, both of same type x2 = y;//still ok,because y is derived form x // x2.b = 1; // error x doesn't have a b member } } class X { int a; X(int i){ a = i;} } class Y extends X { int b; Y(int i) { super(i); b = i; } }
这里,X派生了Y,所以允许赋给x2一个对Y对象的引用.
哪些成员可以访问是由引用变量的类型(而不是他引用的对象类型)决定的,理解这一点十分重要.也就是说,当一个子类对象的引用被赋给一个超类引用变量时,只有权访问对象的那些超类定义的部分.这就是为什么即使x2引用了一个Y对象后,还不能访问b的原因.考虑一下就会发现这是合理的,因为超类不知道子类增加了什么,这也就是程序中代码的最后一行被注释调的原因.
尽管前面的讨论看似深奥,但却有一些重要的实际用途.我们这里就其中一点进行介绍,其他的将在后面设计方法重写时讨论.
把子类引用赋给超类变量常用于在类层次结构中调用构造函数时.如你所知,对于类而言,定义一个把类的对象作为形参的构造函数是很常见的.这就允许类构造对象的副本.这种类的子类就可以利用这一功能,例如,考虑下面的X和Y.这两个类都添加了把对象作为形参的构造函数.
public class test2 { // @param args public static void main(String args[]) { Y one = new Y(10); Y two = new Y(one); System.out.println("one.a="+one.a+" one.b="+one.b); System.out.println("two.a="+two.a+" two.b="+two.b); } } class X { int a; X(int i){ a = i;} X(X obj) { a = obj.a; } } class Y extends X { int b; Y(int i) { super(i); b = i; } Y(Y obj) { super(obj.a); b = obj.b; } }
输出:
one.a=10 one.b=10 two.a=10 two.b=10
该程序中,通过one构造了two,因此他们是一样的.