接口中的 Java 转换

有没有人能解释一下为什么编译器在第一次强制转换时没有抱怨,但是在第二次强制转换时却抱怨了?

interface I1 { }
interface I2 { }
class C1 implements I1 { }
class C2 implements I2 { }


public class Test{
public static void main(String[] args){
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 !!
}
}
33767 次浏览

使用 (I2)强制转换 o1o3时,告诉编译器对象的类实际上是其声明类型的子类,而这个子类实现 I2

Integer类是 期末考试,所以 o3不能是 Integer子类的实例: 编译器知道你在撒谎。然而,C1不是 final,因此 o1 可以是实现 I2C1子类型的一个实例。

如果你使 C1最终,编译器也会抱怨:

interface I1 { }
interface I2 { }
final 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 y = (I2)o3; //compiler complains here !!
I2 x = (I2)o1; //compiler complains too
}
}

这是因为类 Integer是最终的,而 C1不是。因此,Integer 对象不能实现 I2,而如果 C1对象是实现 I2的 C1子类的实例,则 C1对象可以实现 I2。

根据 JLS 5.5.1-参考类型铸造,规则适用于:

  • 如果 T 是类类型,则 | S | < : | T | 或 | T | < : | S | 。否则,将发生编译时错误。

    I2 y = (I2)o3; //compiler complains here !!

在这种情况下,IntegerI2在任何方面都是 毫无关联,因此会发生编译时错误。而且,因为 Integerfinal,所以 IntegerI2之间没有关系。

I2I1可以相关,因为它们都是一个标记接口(没有契约)。

至于已编译的代码,规则如下:

  • 如果 S 不是最终类(8.1.1) ,那么,如果存在一个 T 的超类型 X 和一个 S 的超类型 Y,这样 X 和 Y 都可以证明是不同的参数化类型,并且 X 和 Y 的擦除是相同的,就会发生编译时错误。

So1TI2

希望这个能帮上忙。

根据 JLS 第五章

5.5.1参考型铸造

给定编译时引用类型 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,否则将发生编译时错误。