为什么 Java 在使用 lambda 泛型时选择 Object 参数化类型?

bur*_*mre 3 java generics lambda java-8

假设我有一个接受 java.util.function.Predicate 并返回 CompletableFuture 的方法:

public <R> CompletableFuture<R> call(Predicate<R> request) {
    return new CompletableFuture<>();
}
Run Code Online (Sandbox Code Playgroud)

如果我使用这样的匿名类调用此方法:

Integer a = call(new Predicate<Integer>() {
    @Override
    public boolean test(Integer o) {
        return false;
    }
}).join();
Run Code Online (Sandbox Code Playgroud)

它之所以有效,是因为我必须显式声明谓词的类型。但是,如果我使用 lambda 表达式而不是像这样的匿名类:

Integer a = call(o -> false).join();
Run Code Online (Sandbox Code Playgroud)

它无法编译,因为 Java 认为它是 aPredicate<Object>并返回如下消息:

Error:(126, 42) java: incompatible types: java.lang.Object cannot be converted to java.lang.Integer
Run Code Online (Sandbox Code Playgroud)

我发现了一些解决方法。我可以CompletableFuture显式创建变量而不是链接,或者添加一个不必要的额外参数Class<R>来告诉 Java 我们想要获取哪种类型或强制 lambda 表达式中的类型。

然而我想知道为什么 Java 选择Object而不是Integer在第一个 lambda 示例中,它已经知道我想要获得哪种类型,因此编译器可能会使用最具体的类型,而不是Object因为所有三种解决方法对我来说似乎都很丑陋。

Jes*_*per 5

Java 8 的类型推断存在限制,它不会查看将结果分配给的变量的类型。Object相反,它推断类型参数的类型。

o您可以通过在 lambda 中显式指定参数类型来修复此问题:

Integer a = call((Integer o) -> false).join();
Run Code Online (Sandbox Code Playgroud)

  • @burakemre 天下没有免费的午餐。类型推断需要一些类型信息才能使用。它非常擅长从大多数程序中查找一些内容,但如果您不向它提供任何可以使用的东西,它就无法发挥其魔力。添加额外的类型信息并不是“解决方法”;而是一种解决方法。它为类型推断提供了足够的信息来解决您所要求的问题。偶尔在 lambda 参数上添加清单类型实际上并不是那么具有侵入性。您还可以在调用方法上添加显式类型见证:`receiver.&lt;R&gt;call(...)`,以防干扰较少。 (4认同)
  • “人们对允许推理“链”感兴趣:在 a().b() 中,将类型信息从 b 的调用传递到 a 的调用。这为推理算法的复杂性增加了另一个维度,如部分信息必须在两个方向上传递;只有当 a() 的返回类型的擦除对于所有实例(例如 List)都是固定时才起作用。此功能不太适合 Poly 表达式模型,因为目标类型不能轻易导出;但也许通过额外的增强,它可以在未来添加。” (3认同)
  • @Burak emre 请参阅 https://jcp.org/aboutJava/communityprocess/final/jsr335/index.html D 部分。Java 8 显着增强了 Java 8 类型推断,但对于像这样的链式表达式推断来说还不够。 (2认同)