我最近被以下 Java 代码惊呆了:
interface Common {}
interface A extends Common {}
static class B implements Common {}
static class Impl {
private A a;
public <T extends A> T translate() {
return (T) a;
}
}
static class Usage {
public void use() {
Impl impl = new Impl();
B b = impl.translate(); // Why does this compile?
}
}
Run Code Online (Sandbox Code Playgroud)
我本来期望的是在类型约束Impl.translate不会允许将结果存储在类型B由编译器所接受,考虑到B不延长A。代码UncheckedCastException在运行时抛出一个,而不是编译器错误。
这仅在方法返回类型时发生T;如果它是方法参数:
public <T extends A> void translate(T t) {}
Run Code Online (Sandbox Code Playgroud)
然后,正如预期的那样,B不允许将 的实例作为 的参数translate。
这里发生了什么?为什么 Java 的类型系统允许这样做?
这是可以编译的,因为原则上,一个对象可能既是 aB又是 an A。例如,这个类的一个实例:
static class C extends B implements A {}
Run Code Online (Sandbox Code Playgroud)
对于编译器来说不存在这样的类并不重要。其他人可能可以从依赖项导入此代码并定义C自己,这是有效的,必须允许其工作。在编译时找到对象的实际类并不重要,因为编译器不会进行那种分析。也不会考虑禁止存在此类的修饰符,例如添加final到B,但我不确定原因。可能只是为了降低编译器逻辑的复杂性。
更改A为类会导致编译错误,因为 Java 不允许一个类扩展多个类。
你的泛型T没有被分配。因为有可能有一个两者兼而有之的值,B并且A编译器认为你没问题。
如果您为其指定类型,B则会出现错误。
B b = impl.<B>translate();
Run Code Online (Sandbox Code Playgroud)
Testy.java:16: 错误:类 Impl 中的翻译方法无法应用于给定类型;B b = impl.translate(); ^
必需:未
找到参数:没有参数
原因:显式类型参数 B 不符合声明的边界 A
当类型由参数确定时,您会遇到类似的错误。
public <T extends A> T translate(T t) {
return (T) a;
}
B b = impl.translate(new B());
Run Code Online (Sandbox Code Playgroud)
类型由参数指定,并且参数是一个Bthis 不会编译。
正如另一个答案中提到的,如果两者A都是B类,那么就不会有T可以同时满足两者。
Testy.java:15: 错误:不兼容的类型:推理变量 T 具有不兼容的上限 B,A
B b = impl.translate(); // 为什么会编译?
^
其中 T 是类型变量:
T 扩展了方法 translate() 中声明的 A
| 归档时间: |
|
| 查看次数: |
181 次 |
| 最近记录: |