Java 缩小从类型到接口的引用转换
Java Narrowing Reference Conversion from a type to an interface
试图理解 java 缩小从 class 到接口的转换。 JLS(JLS-5.1.6) 声明:
From any class type C to any non-parameterized interface type K, provided that C is not final and does not implement K.
为了对此进行测试,我创建了一个 class 和一个界面。然后尝试将 class 转换为接口,但得到 运行-time 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()");
}
}
在编译和 运行 上面我收到以下错误消息:
Exception in thread "main" java.lang.ClassCastException: S cannot be cast to T
这是一个不能保证有效的转换,就像将基数 class 的引用转换为子 class 不能保证有效一样。这就是为什么它被认为是缩小转换。
编译器知道转换可能在运行时有效,所以它允许它,但如果它不起作用,ClassCastException
会在运行时抛出。
只有当您将 S
的子 class 实例分配给 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 is not final" - 如果是 final
,就不会有 C
的子 class,所以编译器肯定知道此转换永远无法工作,编译失败。
"does not implement K" - 如果 C
实现了 K
,这不再是收缩转换。它变成了 Widening Reference Conversion,保证在运行时成功。事实上,不需要使用强制转换运算符。一个简单的赋值就可以了。
简单的是:
S s = new S();
T t = (T)s;
鉴于您显示的当前代码,编译器可以知道这个转换没有意义,并且必须在运行时失败。
但事实是:您的案例是一个非常具体的例子。常见用例较少"clear"。正如 Eran 所展示的,构建一个类似的示例非常简单,在该示例中,运行时的转换可能有效,也可能无效,这取决于非常细微的差异。
因此,实用的答案是:编译器 可以 知道程序无效并且稍后会失败的事实并不一定会导致编译器失败。
换句话说:当您设计语言和构建编译器时,总是需要权衡取舍。如:有时不值得在编译时添加非常具体的检查。您宁愿接受更通用的规则,该规则可能会在运行时而不是编译时导致失败。
试图理解 java 缩小从 class 到接口的转换。 JLS(JLS-5.1.6) 声明:
From any class type C to any non-parameterized interface type K, provided that C is not final and does not implement K.
为了对此进行测试,我创建了一个 class 和一个界面。然后尝试将 class 转换为接口,但得到 运行-time 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()");
}
}
在编译和 运行 上面我收到以下错误消息:
Exception in thread "main" java.lang.ClassCastException: S cannot be cast to T
这是一个不能保证有效的转换,就像将基数 class 的引用转换为子 class 不能保证有效一样。这就是为什么它被认为是缩小转换。
编译器知道转换可能在运行时有效,所以它允许它,但如果它不起作用,ClassCastException
会在运行时抛出。
只有当您将 S
的子 class 实例分配给 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 is not final" - 如果是
final
,就不会有C
的子 class,所以编译器肯定知道此转换永远无法工作,编译失败。"does not implement K" - 如果
C
实现了K
,这不再是收缩转换。它变成了 Widening Reference Conversion,保证在运行时成功。事实上,不需要使用强制转换运算符。一个简单的赋值就可以了。
简单的是:
S s = new S();
T t = (T)s;
鉴于您显示的当前代码,编译器可以知道这个转换没有意义,并且必须在运行时失败。
但事实是:您的案例是一个非常具体的例子。常见用例较少"clear"。正如 Eran 所展示的,构建一个类似的示例非常简单,在该示例中,运行时的转换可能有效,也可能无效,这取决于非常细微的差异。
因此,实用的答案是:编译器 可以 知道程序无效并且稍后会失败的事实并不一定会导致编译器失败。
换句话说:当您设计语言和构建编译器时,总是需要权衡取舍。如:有时不值得在编译时添加非常具体的检查。您宁愿接受更通用的规则,该规则可能会在运行时而不是编译时导致失败。