试图了解java缩小了从类到接口的转换。 JLS(JLS-5.1.6)指出:
从任何类类型C到任何非参数化接口类型K,只要C不是最终的并且不实现K。
为了测试这一点,我创建了一个类和一个接口。然后尝试将类强制转换为接口,但会获得运行时ClassCastException。这是我的代码示例。
class NarrowingReferenceConversion
{
public static void main(String args[])
{
S s = new S();
T t = (T)s;
}
}
interface T
{
public void print();
}
class S
{
public void print(){
System.out.println("S.print()");
}
}
在编译并运行以上代码时,我收到以下错误消息:
线程“ main”中的异常java.lang.ClassCastException:S无法转换为T
答案 0 :(得分:3)
这是不能保证有效的转换,就像不能保证将基类的引用强制转换为子类一样。这就是为什么它被认为是缩小转换的原因。
编译器知道转换可能在运行时有效,因此它允许转换,但是如果转换不起作用,则会在运行时抛出ClassCastException
。
仅当您将实现了s
接口的S
子类的实例分配给T
时,转换才能起作用。
class NarrowingReferenceConversion
{
public static void main(String args[])
{
S s = new S2();
T t = (T) s; // this will work, since S2 implements T
}
}
interface T
{
public void print();
}
class S
{
public void print(){
System.out.println("S.print()");
}
}
class S2 extends S implements T
{
}
让我们解释这种转换的两个条件:
“ C不是最终的”-如果它是final
,则不会有C
的子类,因此编译器肯定会知道这种转换永远不会起作用,因此编译失败。
“未实现K”-如果C
实现K
,则不再是缩小转换。它变成Widening Reference Conversion,保证在运行时成功。实际上,不需要使用强制转换运算符。一个简单的任务即可。
答案 1 :(得分:1)
简单的事情是:
S s = new S();
T t = (T)s;
鉴于您显示的当前代码,编译器可以知道此强制转换没有意义,并且必须在运行时失败
但事实是:您的案例是一个非常具体的例子。常见的用例不太“清晰”。如Eran所示,构造一个类似的示例非常简单,其中取决于非常细微的差异,运行时的强制转换是否起作用。
因此,务实的答案是:编译器可以知道程序无效并且以后将失败的事实并不一定会导致编译器失败。
换句话说,当您设计语言并构造编译器时,总是需要权衡取舍。如:有时,不值得在编译时添加非常具体的检查。您宁愿接受一条更通用的规则,该规则可能会在运行时而不是编译时导致失败。