Reputation: 1591
Let's say I have method which takes a java.util.function.Predicate and return CompletableFuture:
public <R> CompletableFuture<R> call(Predicate<R> request) {
return new CompletableFuture<>();
}
If I call this method using an anonymous class like this:
Integer a = call(new Predicate<Integer>() {
@Override
public boolean test(Integer o) {
return false;
}
}).join();
It works because I have to explicitly declare the type of the Predicate. However, if I use lambda expression instead of anonymous class like this:
Integer a = call(o -> false).join();
It doesn't compile because Java thinks that it's a Predicate<Object>
and returns a message like this:
Error:(126, 42) java: incompatible types: java.lang.Object cannot be converted to java.lang.Integer
There are a few workarounds that I found. I may create the CompletableFuture
variable explicitly instead of chaining, or add an unnecessary extra argument Class<R>
that tells Java which type we want to get or force the type in lambda expression.
However I wonder why Java chooses Object
instead of Integer
in the first lambda example, it already knows which type I want to get so the compiler may use the most specific type instead of Object
because all of the three workarounds seems ugly to me.
Upvotes: 3
Views: 2580
Reputation: 122439
Other people have answered how to force the lambda to the right type. However, I would argue that Predicate<Object>
is not "wrong" for that predicate and you should be allowed to use it -- you have a predicate on all objects -- it should work on all objects including Integer
s; why do you need to make a more restricted predicate on Integer
s? You shouldn't.
I would argue that the real problem is the signature of your call
method. It's too strict. Predicate
is a consumer of its type parameter (it only has a method that takes its type parameter type and returns boolean
); therefore, according to the PECS rule (Producer extends
, consumer super
), you should always use it with a super
bounded wildcard.
So you should declare your method like this:
public <R> CompletableFuture<R> call(Predicate<? super R> request)
No matter what code you had in this method, it will still compile after changing the bound to super
, because Predicate
is a consumer. And now, you don't need to force it to be Predicate<Integer>
-- you can use a Predicate<Object>
and still have it return a CompletableFuture<Integer>
.
Upvotes: 3
Reputation: 206796
There are limits to Java 8's type inference, and it does not look at the type of the variable that you assign the result to. Instead, it infers the type Object
for the type parameter.
You can fix it by explicitly specifying the type of the argument o
in the lambda:
Integer a = call((Integer o) -> false).join();
Upvotes: 5