Почему ключевое слово var в Java не может быть назначено лямбда-выражением?

Разрешено назначать var в Java 10 с помощью строки типа:

var foo = "boo";

Хотя ему не разрешено назначать его с помощью лямбда-выражения, такого как:

var predicateVar = apple -> apple.getColor().equals("red");

Почему он не может вывести тип привязки лямбда или метода, если он может сделать вывод, что остальные, такие как String, ArrayList, класс пользователя и т.д.?

Ответ 1

Из вывода типа локального переменного JEP:

Процесс вывода, по существу, просто дает переменной тип выражения инициализатора. Некоторые тонкости:

  • Инициализатор не имеет целевого типа (потому что мы еще не сделали вывод). Полимерные выражения, требующие такого типа, такие как lambdas, ссылки на методы и инициализаторы массивов, вызывают ошибку.

Поскольку выражение лямбда само по себе не имеет типа, оно не может быть выведено для var.


... Аналогичным образом может быть установлено правило по умолчанию.

Конечно, вы можете придумать способ обойти это ограничение. Почему разработчики приняли решение не делать этого, это действительно до спекуляции, если только кто-то, кто принимал участие в принятии решений, не может ответить здесь. Если вам все равно интересно, вы можете спросить об этом в одном из списков рассылки openjdk: http://mail.openjdk.java.net/mailman/listinfo

Если бы я мог догадаться, они, вероятно, не хотели бы привязывать лямбда-вывод в контексте var к определенному набору типов функциональных интерфейсов, что исключало бы любые типы функциональных интерфейсов сторонних разработчиков. Лучшим решением было бы вывести общий тип функции (т.е. (Apple) → boolean), который может быть преобразован в совместимый тип функционального интерфейса. Но JVM не имеет таких типов функций, и решение не выполнять их уже было сделано во время проекта, который создал лямбда-выражения. Опять же, если вас интересуют конкретные причины, спросите разработчиков.

Ответ 2

Это не имеет ничего общего с var. Это связано с тем, имеет ли лямбда отдельный тип. Способ работы var заключается в том, что он вычисляет автономный тип инициализатора на RHS и передает это.

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

Если вы попытаетесь:

Object o = (String s) -> s.length();

вы также получаете ошибку типа, потому что компилятор понятия не имеет, какой функциональный интерфейс вы намерены преобразовать лямбда.

Просить о выводе с помощью var просто усложняет работу, но поскольку на более простой вопрос ответить нельзя, тем сложнее и то, и другое.

Обратите внимание, что вы можете указать целевой тип другими способами (например, приведение), а затем он будет работать:

var x = (Predicate<String>) s -> s.isEmpty();

потому что теперь RHS имеет автономный тип. Но вам лучше предоставить целевой тип, указав x тип манифеста.

Ответ 3

Для всех, кто говорит это, невозможно, нежелательно или нежелательно, я просто хочу указать, что Scala может вывести лямбда-тип, указав только тип аргумента:

val predicateVar = (apple: Apple) => apple.getColor().equals("red")

И в Haskell, потому что getColor будет автономной функцией, не привязанной к объекту, и потому что она делает полный вывод Hindley-Milner, вам не нужно указывать даже тип аргумента:

predicateVar = \apple -> getColor apple == "red"

Это чрезвычайно удобно, потому что это не простые типы, которые раздражают программистов, чтобы явным образом указать, это более сложные.

Другими словами, это не функция Java 10. Это ограничение их реализации и предыдущих вариантов дизайна.

Ответ 4

Чтобы ответить на этот вопрос, нам нужно вдаваться в детали и понять, что такое лямбда и как оно работает.

Сначала мы должны понять, что такое лямбда:

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

Имея это в виду, давайте сделаем следующий шаг:

Это отлично работает, как в случае с Runnable, я могу просто создать новый поток, такой как этот new Thread(()->{//put code to run here}); вместо создания целого нового объекта для реализации функционального интерфейса. Это работает, поскольку компилятор знает, что Thread() принимает объект типа Runnable, поэтому он знает, какой тип должен иметь выражение лямбда.

Однако в случае назначения лямбда локальной переменной компилятор не знает, какой функциональный интерфейс реализуется этой лямбдой, поэтому он не может определить, какой тип var должен быть. Так как, возможно, он реализует функциональный интерфейс, созданный пользователем или, возможно, это runnable интерфейс, просто не существует способа узнать.

Вот почему lambdas не работают с ключевым словом var.

Ответ 5

Как уже упоминалось несколько человек, какой тип должен вызывать var и почему?

Заявление:

var predicateVar = apple -> apple.getColor().equals("red");

неоднозначно, и нет веской причины, по которой компилятор должен выбрать Function<Apple, Boolean> over Predicate<Apple> или наоборот, предполагая, что идентификатор apple в лямбда представляет собой Apple isntance.

Другая причина заключается в том, что сама лямбда не имеет понятного типа, поэтому компилятор не может это сделать.

Кроме того, "если это было возможно", представьте себе накладные расходы, поскольку компилятор должен будет пройти через все функциональные интерфейсы и определить, какой функциональный интерфейс является наиболее подходящим каждый раз, когда вы назначаете лямбда переменной var.

Ответ 6

Потому что это не-функция:

Эта обработка будет ограничена локальными переменными с инициализаторами, индексами в расширенном for-loop и locals, объявленными в традиционном for-loop; он не будет доступен для форматов методов, конструкторских форм, типов возвращаемых методов, полей, форматов catch или любого другого объявления переменной.

http://openjdk.java.net/jeps/286