接口中的 Java 强制转换
Posted
技术标签:
【中文标题】接口中的 Java 强制转换【英文标题】:Java casting in interfaces 【发布时间】:2013-04-17 16:17:05 【问题描述】:有人可以向我解释一下编译器如何在第一次强制转换时不抱怨,但在第二次转换时却抱怨吗?
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 !!
【问题讨论】:
运行这个,你会遇到ClassCastException
,这是一个RuntimeException
。
@BuhakeSindi 你错了
@BuhakeSindi OP 说编译器向我抱怨说你没有机会运行它。
@emory,即使他可以通过修复有编译器错误的项目来编译它,它上面的行也会导致ClassCastException
。
@BuhakeSindi 你错了,因为你说了你不应该说的话。水是湿的。但在这种情况下谈论这个问题是错误的。好的?显然,提问者知道这件事是因为他做了一个实验。
【参考方案1】:
当您将o1
和o3
转换为(I2)
时,您告诉编译器该对象的类实际上是其声明类型的子类,并且该子类实现了I2
。
Integer
类是final,所以o3
不能是Integer
的子类的实例:编译器知道你在撒谎。 C1
然而不是最终的,所以 o1
可能是实现 I2
的 C1
的子类型的一个实例。
如果你将C1
设为final,编译器也会报错:
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
【讨论】:
"整数是最终的,所以 o3 不可能是接口 I2" 这在这种形式中是非常错误的。不是意思,而是措辞。 @WilQu 我不是在抱怨错字,而是在抱怨答案的措辞。如果你看看下面,你会发现艾蒂安对同一件事的措辞更加清晰。你的可能会被误解,因此会产生误导,因为你没有提到(例如)Integer
的 类 是最终的(这对我和你来说很明显,但可能不是每个人都知道)和 “没有机会成为接口”部分不携带太多信息;事实上,有问题的信息隐藏在第二句话中。人们甚至可能认为final
类不能实现接口,这是不正确的。
@Powerslave 感谢您的 cmets,我再次编辑了我的答案。我试图添加更多解释,希望它不会使我的答案更加混乱。
@WilQu 太棒了!原来是一个非常好的解释:)【参考方案2】:
根据JLS chapter 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,否则会发生编译时错误。
【讨论】:
【参考方案3】:那是因为 Integer
是 final 而 C1
不是。因此,Integer 对象不能实现 I2,而 C1 对象如果是实现 I2 的 C1 子类的实例则可以。
【讨论】:
【参考方案4】:根据JLS 5.5.1 - Reference Type casting,适用规则:
如果 T 是类类型,那么 |S| <:>
I2 y = (I2)o3; //compiler complains here !!
在这种情况下,Integer
和 I2
在任何方面都不相关,因此会发生编译时错误。另外,因为Integer
是final
,所以Integer
和I2
之间没有关系。
I2
和 I1
可以关联,因为它们都是标记接口(没有合同)。
对于编译后的代码,规则如下:
如果 S 不是最终类(第 8.1.1 节),那么,如果存在 T 的超类型 X 和 S 的超类型 Y,使得 X 和 Y 都可证明是不同的参数化类型,并且 X 和 Y 的擦除相同,就会发生编译时错误。S
是 o1
和 T
是 I2
。
希望这会有所帮助。
【讨论】:
以上是关于接口中的 Java 强制转换的主要内容,如果未能解决你的问题,请参考以下文章