繁体   English   中英

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

[英]Why Java chooses Object parameterized type when using generics with lambda?

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

public <R> CompletableFuture<R> call(Predicate<R> request) {
    return new CompletableFuture<>();
}

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

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

它的工作原理是因为我必须明确声明谓词的类型。 但是,如果我使用lambda表达式而不是像这样的匿名类:

Integer a = call(o -> false).join();

它不会编译,因为Java认为它是Predicate<Object>并返回如下消息:

Error:(126, 42) java: incompatible types: java.lang.Object cannot be converted to java.lang.Integer

我找到了一些解决方法。 我可能会显式创建CompletableFuture变量而不是进行链接,或者添加不必要的额外参数Class<R>来告诉Java我们要获取哪种类型或在lambda表达式中强制使用该类型。

但是我不知道为什么Java在第一个lambda示例中选择了Object而不是Integer ,它已经知道我要获取哪种类型,因此编译器可以使用最特定的类型而不是Object因为这三种解决方法对我来说都很难看。

Java 8的类型推断有一定的局限性,它不会查看将结果分配给的变量的类型。 相反,它推断出类型参数的类型Object

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

Integer a = call((Integer o) -> false).join();

其他人已经回答了如何将lambda强制为正确的类型。 但是,我认为Predicate<Object>对于该谓词并不“错误”,你应该被允许使用它 - 你有一个所有对象的谓词 - 它应该适用于所有对象,包括Integer ; 为什么需要对Integer作出更严格的谓词? 你不应该。

我认为真正的问题是你的call方法的签名。 这太严格了。 Predicate是其类型参数的使用者 (它只有一个采用其类型参数类型并返回boolean ); 因此,根据PECS规则(“生产者extends ,“消费者super ),您应始终将其与super界通配符一起使用。

因此,您应该这样声明您的方法:

public <R> CompletableFuture<R> call(Predicate<? super R> request)

无论您使用此方法使用什么代码,都将绑定更改为super之后仍将编译,因为Predicate是使用者。 现在,您无需强制将其设置为Predicate<Integer> -您可以使用Predicate<Object>并使它返回CompletableFuture<Integer>

暂无
暂无

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

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