Java中类型、向上转型与类/接口的关系澄清
先明确前提的类与接口定义:
// 接口定义 interface isLiving {} interface isCute {} // 父类实现isLiving接口 class Animal implements isLiving {} // 子类继承Animal并实现isCute接口 class Dog extends Animal implements isCute {}
在Java里,引用变量有编译类型(声明时的类型)和运行时类型(实际创建的对象类型)之分:对象本身的实际类型从创建后就不会改变,而引用变量的编译类型可以通过转型改变,但这只是限制了编译阶段能访问的成员,不会改变对象本身的类型归属。
针对你的三个问题逐一解答:
问题1:Animal dog1 = new Dog(); 中dog1的类型是否同时为Animal和isLiving?
首先纠正一个认知偏差:dog1是引用变量,它的编译类型是Animal,但它指向的对象实际类型是Dog。
从对象本身的类型归属来说,这个Dog对象同时属于Dog、Animal、isLiving、isCute这四个类型范畴(因为Dog继承Animal,Animal实现isLiving,Dog自己实现isCute)。
而从编译阶段的访问限制来看,dog1这个引用只能被当作Animal或isLiving类型来使用(因为Animal实现了isLiving),编译时无法直接访问Dog或isCute的独有方法,但运行时调用方法时依然会执行Dog的实现。
问题2:Dog dog2 = new Dog(); 中dog2的类型是同时为Dog和isCute,还是同时为Dog、isCute和isLiving?
dog2的编译类型和实际类型都是Dog。由于Dog继承了Animal,而Animal实现了isLiving接口,所以Dog类自动继承了Animal的接口实现,因此这个Dog对象的类型同时属于Dog、Animal、isLiving、isCute。
简单说:dog2对应的对象同时具备Dog、isCute、isLiving这三个类型(Animal作为父类类型也包含在内)。
问题3:将dog2向上转型为Animal后,其类型是否为Animal和isLiving?
假设转型代码为:Animal dog3 = dog2;
此时dog3的编译类型是Animal,但它指向的对象实际类型依然是Dog。
从编译阶段的访问限制来看,dog3只能被当作Animal或isLiving类型使用;但对象本身的实际类型还是Dog,依然具备Dog、isCute的类型属性——只是转型后的引用无法直接访问这些类型的独有方法,除非通过强制向下转型恢复为Dog类型。
所以从引用的编译类型角度,它的可识别类型是Animal和isLiving;但从对象本身的类型归属来说,它依然包含所有父类和接口类型。
内容的提问来源于stack exchange,提问作者Mr. Brown

