让我说我有一个方法,它接受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
,它已经知道我想要哪种类型,因此编译器可以使用最具体的类型而不是{{1因为所有这三个变通办法对我来说都很难看。
答案 0 :(得分:4)
Java 8的类型推断存在限制,它不会查看您为其分配结果的变量的类型。相反,它会为类型参数推断类型Object
。
您可以通过在lambda中明确指定参数o
的类型来修复它:
Integer a = call((Integer o) -> false).join();
答案 1 :(得分:3)
其他人已经回答了如何将lambda强制为正确的类型。但是,我认为Predicate<Object>
不是&#34;错误&#34;对于该谓词,你应该被允许使用它 - 你有一个所有对象的谓词 - 它应该适用于所有对象,包括Integer
s;为什么你需要在Integer
s上制作一个更受限制的谓词?你不应该。
我认为真正的问题是你的call
方法的签名。它太严格了。 Predicate
是其类型参数的使用者(它只有一个采用其类型参数类型并返回boolean
)的方法;因此,根据PECS规则(生产者extends
,消费者super
),您应始终将其与super
有界通配符一起使用。
所以你应该声明你的方法:
public <R> CompletableFuture<R> call(Predicate<? super R> request)
无论您使用此方法中的代码,在将绑定更改为super
后仍会编译,因为Predicate
是使用者。现在,您不需要强制它Predicate<Integer>
- 您可以使用Predicate<Object>
并仍然让它返回CompletableFuture<Integer>
。