79

有人可以向我解释一下编译器如何在第一次转换时不抱怨,但在第二次转换时抱怨吗?

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 !!
     }
}
4

4 回答 4

150

当你转换o1o3with 时(I2),你告诉编译器对象的类实际上是它声明的类型的子类,并且这个子类实现了I2

该类Integerfinal,因此o3不能是 的子类的实例Integer:编译器知道您在撒谎。C1然而不是最终的,因此o1 可能C1是该 implements的子类型的一个实例I2

如果你 make C1final,编译器也会抱怨:

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
     }
}
于 2013-04-24T08:01:02.230 回答
36

根据JLS 第 5 章

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,否则会发生编译时错误。

于 2013-04-24T08:06:53.883 回答
24

那是因为 classInteger是 final 而C1不是。因此,Integer 对象不能实现 I2,而 C1 对象如果它是实现 I2 的 C1 子类的实例则可以。

于 2013-04-24T08:03:50.710 回答
15

根据JLS 5.5.1 - Reference Type cast,规则适用:

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

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

在这种情况下,Integerand在任何方面I2都不相关,因此会发生编译时错误。此外,因为Integeris ,和final之间没有关系。IntegerI2

I2并且I1可以是相关的,因为两者都是标记接口(没有合同)。

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

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

So1TI2

希望这可以帮助。

于 2013-04-24T08:06:37.107 回答