Java8 - Как явный тип соответствует одному варианту, а не другому типу? - PullRequest
3 голосов
/ 22 апреля 2020

У меня есть простой фрагмент, как показано ниже. Я упомянул this

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

Компилируется без проблем.

  1. Я предполагаю, что мы говорим компилятору, что doc имеет тип Document. Зачем это нужно?

Но если я сделаю следующее, это выдаст неоднозначную ошибку. Я упомянул это Но не мог точно сказать и понять.

collection.find(findQuery).forEach(list::add);

Может кто-нибудь объяснить, почему не работает второе утверждение?

Есть ли лучший способ написания первого [рабочий one] ?

Java версия: 1.8.0_231

операторы импорта:

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

Ответы [ 3 ]

3 голосов
/ 22 апреля 2020

Проблема в том, что forEach это просто Consumer, который имеет единственный метод void accept(T element), и вы пытаетесь вернуть значение.

«Неоднозначная» ошибка в первом версия была с учетом других сообщений здесь .

Вы можете сделать (я бы посчитал, что больше idiomati c)

return StreamSupport.stream(collection.find(findQuery).spliterator(), false)
                    .collect(Collectors.toList());
2 голосов
/ 22 апреля 2020

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)) успешно выбирает / связывает подпись Consumer<Document> вместо той, которая имеет Block<? super Document>, я предполагаю, что она должна делать с обобщенными c границами (но я все еще читаю об этом).

Выбор для вас должен быть легким, потому что версия Block<? super Document> устарела.

0 голосов
/ 22 апреля 2020

Как указано в lamba target typing

, компилятор Java использует целевой тип контекста или ситуации, в которой было найдено лямбда-выражение. Из этого следует, что вы можете использовать лямбда-выражения только в ситуациях, когда компилятор Java может определить целевой тип

Во втором фрагменте компилятор не может определить целевой тип лямбда-выражения. Почему?

Потому что, когда вы используете Ссылка на метод , JRE выводит аргументы типа метода, которые в этом случае неоднозначны (например, ссылка на метод работает хорошо, только если есть не однозначный вывод)

JRE не знает, используете ли вы:

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

ИЛИ

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

Вот почему ваш первый фрагмент работает. Кастом Object. Вы избавляетесь от этой двусмысленности.

...