繁体   English   中英

为什么 Java 允许这段代码与 generics 一起编译?

[英]Why does Java allow this code with generics to compile?

我最近被以下 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?
  }
}

考虑到B不扩展A ,我本来希望Impl.translate上的类型约束不允许将结果存储在类型B中以被编译器接受。 代码在运行时抛出UncheckedCastException而不是编译器错误。

这只发生在方法返回类型T时; 如果它是方法参数,而不是:

  public <T extends A> void translate(T t) {}

然后,正如预期的那样,不允许B的实例作为 t translate的参数。

这里发生了什么? 为什么 Java 的类型系统允许这样做?

这是可以编译的,因为原则上 object 可能既是B又是A 例如,此 class 的一个实例:

static class C extends B implements A {}

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

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

您的通用T没有被分配。 由于可能有一个BA的值,编译器假设你没问题。

如果将类型分配给B它,则会出现错误。

B b = impl.<B>translate(); 

Testy.java:16:错误:class Impl 中的方法转换不能应用于给定类型; B b = impl.translate(); ^
必需:无 arguments
发现:没有 arguments
原因:显式类型参数 B 不符合声明的边界 A

当类型由参数确定时,您会遇到类似的错误。

public <T extends A> T translate(T t) {
    return (T) a;
  }

B b = impl.translate(new B());

类型是由参数分配的,参数是B这不会编译。

如另一个答案中所述,如果AB都是类,则不会有T可以同时满足两者。

Testy.java:15:错误:不兼容的类型:推理变量 T 具有不兼容的上限 B,A
B b = impl.translate(); // 为什么会编译?
^
其中 T 是一个类型变量:
T 扩展了方法 translate() 中声明的 A

TL;DR 答案

关键是可能存在一个扩展 [ B ] 并实现 [ A ] 的 class,而理论类型就是T的推断。” — @JornVernee 2017 年 7 月 3 日 14:43

长答案

Jorn Vernee 实际上在 Java 团队的 Oracle 工作。 他在TL;DR中引用的评论是他雇主第 18 章中最简洁、最容易理解的提炼。你会发现类型推断

如果你对类型系统和 lambda 微积分了如指掌,并且想要完整的细节,那么第 18 章就在你身边。

在 Vernee 的TL;DR的专家简洁和 JLS 令人头痛的严谨性之间,是用户 @Radiodef 对 JLS 的缩减、合并和解决过程的逐点摘要

@JornVernee 和 @Radiodef 对 JLS 在这个谜题背后的推理的解释,是(对我而言)这个问题的七个或八个(至少)重复的几十个其他答案中最清晰、最容易理解的:

  1. 为什么这个通用代码在 java 8 中编译?
  2. 通用返回类型上限 - 接口与 class - 令人惊讶的有效代码
  3. 明显的类型冲突,但编译
  4. 为什么这个带有绑定的泛型方法可以返回任何类型?
  5. 字符串被分配给列表而没有编译错误
  6. 这是 Java 泛型类型推断编译器错误吗?
  7. Java 类型推断:参考在 Java 8 中不明确,但在 Java 7 中不明确

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM