有人可以向我解释编译器在第一次投射中不会抱怨,但在第二次投射中是否会抱怨?
interface I1 { }
interface I2 { }
class C1 implements I1 { }
class C2 implements I2 { }
public class Test{
public static void main(){
C1 o1 = new C1();
C2 o2 = new C2();
Integer o3 = new Integer(4);
I2 x = (I2)o1; //compiler does not complain
I2 y = (I2)o3; //compiler complains here !!
}
}
答案 0 :(得分:145)
当您使用o1
转换o3
和(I2)
时,您告诉编译器该对象的类实际上是其声明类型的子类,并且该子类实现了{{ 1}}。
I2
类是 final ,因此Integer
不能是o3
的子类的实例:编译器知道你在撒谎。 Integer
不是最终版,因此C1
可以是实现o1
的{{1}}子类型的实例。
如果你让C1
成为最终版,编译器也会抱怨:
I2
答案 1 :(得分:33)
给定编译时引用类型S(源)和编译时引用类型T(目标),如果由于以下规则而没有发生编译时错误,则从S到T存在转换转换。 如果T是接口类型:
如果S不是最终类(第8.1.1节),那么,如果存在T的超类型X和S的超类型Y,那么X和Y都可以证明是不同的参数化类型,并且X和Y的擦除是相同的,发生编译时错误。
否则,强制转换在编译时总是合法的(因为即使S没有实现T,也可能是S的子类)。
如果S是最终类(第8.1.1节),则S必须实现T,否则会发生编译时错误。
答案 2 :(得分:22)
那是因为班级Integer
是最终的,C1
不是。因此,Integer对象不能实现I2,而C1对象可以是C1实现I2的C1子类的实例。
答案 3 :(得分:15)
根据JLS 5.5.1 - Reference Type casting,规则适用:
如果T是类类型,则为| S | &lt ;: | T |,或| T | <:| S |。否则,将发生编译时错误。
I2 y = (I2)o3; //compiler complains here !!
在这种情况下,Integer
和I2
以任何方式无关,因此会发生编译时错误。另外,因为Integer
是final
,Integer
和I2
之间没有任何关系。
I2
和I1
可以相关,因为它们都是标记界面(没有合约)。
对于编译的代码,规则如下:
S
为o1
,T
为I2
。
希望这有帮助。