为什么 Java 允许使用泛型编译这段代码?

moo*_*ple 6 java generics

我最近被以下 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 的类型系统允许这样做?

Dou*_*las 5

这是可以编译的,因为原则上,一个对象可能既是 aB又是 an A。例如,这个类的一个实例:

static class C extends B implements A {}
Run Code Online (Sandbox Code Playgroud)

对于编译器来说不存在这样的类并不重要。其他人可能可以从依赖项导入此代码并定义C自己,这是有效的,必须允许其工作。在编译时找到对象的实际类并不重要,因为编译器不会进行那种分析。也不会考虑禁止存在此类的修饰符,例如添加finalB,但我不确定原因。可能只是为了降低编译器逻辑的复杂性。

更改A为类会导致编译错误,因为 Java 不允许一个类扩展多个类。


mat*_*att 1

你的泛型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