Java8 - 显式类型如何匹配一种变体 - 而不是其他类型?

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

我有一个简单的片段如下。我提到了 this

List<Document> list = new LinkedList<Document>();
FindIterable<Document> itr = collection.find(findQuery)
                       .forEach((Document doc) -> list.add(doc));
return list;

编译没有任何问题。

  1. 我想我们是在告诉编译器 docDocument 类型。 为什么需要它?

但是如果我执行下面的操作,它会抛出不明确的错误。我提到了 但无法准确联系和理解。

collection.find(findQuery).forEach(list::add);
  1. 谁能解释为什么第二个语句不起作用?

  2. 有没有更好的方法来写第一个[working one]?

Java版本:1.8.0_231

导入语句:

import java.util.List;
import java.util.Optional;
import com.mongodb.client.FindIterable;
import org.bson.Document;

问题是 forEach 只是一个 Consumer,它只有一个方法 void accept(T element),而您正在尝试 return 一个值。

第一个版本中的 "ambiguous" 错误是 subject to other posts here

你可以做到(我认为这更符合地道)

return StreamSupport.stream(collection.find(findQuery).spliterator(), false)
                    .collect(Collectors.toList());

FindIterable继承两个forEach方法:

您可以使用任一方法重写您的参数

Consumer<Document> consumer = documents::add;
Block<Document> block = list::add;

两者都行。这些也将起作用:

.forEach((Consumer<Document>) doc -> list.add(doc))
.forEach((Consumer<Document>) list::add);

但是,当您调用 forEach(list::add)forEach(doc -> list.add(doc)) 时,编译器无法选择哪个重载将确定方法引用的目标类型(因为表达式在该上下文中与两者兼容)。

现在,我不是 100% 确定为什么 .forEach((Document doc) -> list.add(doc)) 成功 selects/links Consumer<Document> 的签名而不是带有 Block<? super Document> 的签名,我正在推测与通用边界有关(但我仍在阅读此内容)。

您的选择应该很容易,因为 Block<? super Document> 版本已弃用。

因为它在 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

在您的第二个代码段中,编译器无法确定 lambda 的目标类型。为什么?

因为当您使用 Method Reference 时,JRE 会推断出方法类型参数,在这种情况下是不明确的(例如,方法引用仅在没有明确的推断时才有效)

JRE 不知道您是否使用:

com.mongodb.client.MongoIterable.forEach(Block<? super TResult>)

java.lang.Iterable.forEach(Consumer<? super T>)

这就是您的第一个代码段有效的原因。通过投射你的 Object。你摆脱了这种歧义。