Это не совсем только потому, что вы звоните negate()
. Посмотрите на эту версию, которая очень близка к вашей, но компилируется:
Predicate<String> predicate = str -> str.length() <= 5;
names.removeIf(predicate.negate());
Разница между этой и вашей версией? Речь идет о том, как лямбда-выражения получают свои типы («целевой тип»).
Как вы думаете, что это делает?
(str -> str.length() <= 5).negate()?
Ваш текущий ответ таков: «он вызывает negate()
на Predicate<String>
определяется выражением str -> str.length() <= 5
". Правильно? Но это только потому, что ты это хотел сделать. Компилятор не знает, что . Почему? Потому что это может быть что угодно. Мой собственный ответ на поставленный выше вопрос может быть следующим: «он вызывает negate
для моего другого функционального типа интерфейса ... (да, пример будет немного странным)
interface SentenceExpression {
boolean checkGrammar();
default SentenceExpression negate() {
return ArtificialIntelligence.contradict(explainSentence());
};
}
Я мог бы использовать тот же самый лямбда-выражение names.removeIf((str -> str.length() <= 5).negate());
, но означающее str -> str.length() <= 5
быть SentenceExpression
, а не Predicate<String>
.
Объяснение: (str -> str.length() <= 5).negate()
не означает str -> str.length() <= 5
a Predicate<String>
. И вот почему я сказал, что это может быть что угодно, включая мой функциональный интерфейс выше.
Назад к Java ... Вот почему лямбда-выражения имеют понятие «тип цели», которое определяет механику, с помощью которой лямбда-выражения понимается компилятором как данный тип функционального интерфейса (т. е. как вы помогаете компилятору узнать, что выражение является Predicate<String>
, а не SentenceExpression
или чем-то еще, что может быть). Вы можете найти полезным прочитать Что подразумевается под целевым типом лямбда и контекстом целевого типа в Java? и Java 8: типизация цели
Один из контекстов, в которых целевые типы вывод (если вы прочитайте ответы на эти посты) - это контекст вызова, где вы передаете лямбда-выражение в качестве аргумента для параметра типа функционального интерфейса, и это то, что применимо к names.removeIf(((str -> str.length() <= 5)));
: это просто лямбда-выражение, данное в качестве аргумента для метод, который принимает Predicate<String>
. Это не относится к оператору, который не компилируется.
Итак, другими словами ...
names.removeIf(str -> str.length() <= 5);
использует лямбда-выражение в месте, где тип аргумента четко определяет, что ожидается, что тип лямбда-выражения будет (т. е. целевой тип str -> str.length() <= 5
явно Predicate<String>
).
Однако (str -> str.length() <= 5).negate()
не является лямбда-выражением, это просто выражение, которое происходит с используйте лямбда-выражение. Это означает, что str -> str.length() <= 5
в этом случае находится не в контексте вызова, который определяет целевой тип лямбда-выражения (как в случае вашего последнего оператора). Да, компилятор знает, что removeIf
требуется Predicate<String>
, и он точно знает, что все выражение, переданное методу, должно быть Predicate<String>
, но он не будет предполагать, что любое лямбда-выражение в выражении аргумента будет Predicate<String>
(даже если вы трактуете его как предикат, вызывая для него negate()
; это может быть все, что совместимо с лямбда-выражением).
Вот почему вы набираете лямбду с помощью необходимо явное приведение (или иначе, как в первом контрпримере, который я дал).