Java8 - How does explicit type matches one variant - not other type?

好久不见. 提交于 2020-06-16 03:02:10

问题


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.

  1. I guess that we are telling compiler that doc is of type Document. 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);
  1. Could anyone please explain why second statement is not working?

  2. 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

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!