问题
I have a simple snippet as below. I referred this
List<Document> list = new LinkedList<Document>();
FindIterable<Document> itr = collection.find(findQuery)
.forEach((Document doc) -> list.add(doc));
return list;
It compiles without any issues.
- I guess that we are telling compiler that
doc
is of typeDocument
. Why is it needed?
But If I do the below, it throws ambiguous error. I referred this But couldn't relate and understand exactly.
collection.find(findQuery).forEach(list::add);
Could anyone please explain why second statement is not working?
is there any better way of writing the first one [working one]?
Java version: 1.8.0_231
import statements:
import java.util.List;
import java.util.Optional;
import com.mongodb.client.FindIterable;
import org.bson.Document;
回答1:
FindIterable
inherits two forEach
methods:
com.mongodb.client.MongoIterable.forEach(Block<? super TResult>)
java.lang.Iterable.forEach(Consumer<? super T>)
You could rewrite your paramter with either
Consumer<Document> consumer = documents::add;
Block<Document> block = list::add;
And either will work. These too will work:
.forEach((Consumer<Document>) doc -> list.add(doc))
.forEach((Consumer<Document>) list::add);
However, when you call forEach(list::add)
or forEach(doc -> list.add(doc))
, the compiler is unable to pick which overload will determine the method reference's target type (because the expression is compatible with both in that context).
Now, I'm not 100% sure why .forEach((Document doc) -> list.add(doc))
successfully selects/links the signature of Consumer<Document>
instead of the one with Block<? super Document>
, I'm surmizing it has to do with the generic bounds (but I'm still reading on this).
The choice for you should be easy because the Block<? super Document>
version is deprecated.
回答2:
The problem is that forEach
is just a Consumer
, which has a single method void accept(T element)
, and you're trying to return a value.
The "ambiguous" error in the first version was subject to other posts here.
You can do (I'd consider that more idiomatic)
return StreamSupport.stream(collection.find(findQuery).spliterator(), false)
.collect(Collectors.toList());
回答3:
As it is explicited in the lamba target typing
the Java compiler uses the target type of the context or situation in which the lambda expression was found. It follows that you can only use lambda expressions in situations in which the Java compiler can determine a target type
In your second snippet, the compiler cannot determine the target type of the lambda. Why?
Because when you use Method Reference the JRE infers the method type arguments, which in this case are ambiguous (e.g method reference works well only if there is non-ambiguous inference)
The JRE don't know if your using :
com.mongodb.client.MongoIterable.forEach(Block<? super TResult>)
OR
java.lang.Iterable.forEach(Consumer<? super T>)
That's why your first snippet works. By casting your Object
. You get rid of this ambiguity.
来源:https://stackoverflow.com/questions/61359809/java8-how-does-explicit-type-matches-one-variant-not-other-type