Являются ли замыкания в Java 8 действительно первоклассными значениями или это всего лишь синтаксический сахар?
Лямбда-выражение Java 8 и первоклассные значения
Ответы (5)
Я бы сказал, что замыкания Java 8 («лямбды») не являются ни простым синтаксическим сахаром, ни первоклассными значениями.
Я рассмотрел проблему синтаксического сахара в ответе на другой вопрос StackExchange.
Что касается того, являются ли лямбды «первоклассными», это действительно зависит от вашего определения, но я приведу аргумент, что лямбды на самом деле не являются первоклассными.
В некотором смысле лямбда хочет быть функцией, но Java 8 не добавляет типы функций. Вместо этого лямбда-выражение преобразуется в экземпляр функционального интерфейса. Это позволило добавить лямбда-выражения в Java 8 с небольшими изменениями в системе типов Java. После преобразования результатом является ссылка, как и ссылка любого другого типа. На самом деле использование Lambda — например, в методе, которому в качестве параметра было передано лямбда-выражение — неотличимо от вызова метода через интерфейс. Метод, который получает параметр типа функционального интерфейса, не может сказать, было ли ему передано лямбда-выражение или экземпляр некоторого класса, реализующего этот функциональный интерфейс.
Дополнительные сведения о том, являются ли лямбда-выражения объектами, см. в ответе на часто задаваемые вопросы о Lambda на этот вопрос.
Учитывая, что лямбды преобразуются в объекты, они наследуют (буквально) все характеристики объектов. В частности, объекты:
- иметь различные методы, такие как
equals
,getClass
,hashCode
,notify
,toString
иwait
- иметь идентификационный хэш-код
- может быть заблокирован блоком
synchronized
- можно сравнить с помощью операторов
==
и!=
иinstanceof
и так далее. На самом деле все это не имеет отношения к предполагаемому использованию лямбда-выражений. Их поведение по существу не определено. Вы можете написать программу, которая использует любой из них, и вы получите какой-то результат, но результат может отличаться от релиза к релизу (или даже от запуска к запуску!).
Говоря более кратко, в Java объекты имеют идентичность, но значения (особенно значения функций, если они должны существовать) не должны иметь никакого понятия идентичности. В Java 8 нет типов функций. Вместо этого лямбда-выражения преобразуются в объекты, поэтому у них много багажа, который не имеет отношения к функциям, особенно к идентичности. Мне это не кажется "первоклассным".
Обновление от 24 октября 2013 г.
Я размышлял над этой темой с тех пор, как опубликовал свой ответ несколько месяцев назад. С технической точки зрения все, что я написал выше, верно. Вывод, вероятно, выражается более точно, поскольку лямбда-выражения Java 8 не являются чистыми (в отличие от первоклассных) значениями, потому что они несут с собой много объектного багажа. Однако то, что они нечисты, не означает, что они не первоклассные. Рассмотрим определение Википедии функции первого класса. Вкратце, перечисленные там критерии признания функций первоклассными — это способности:
- передавать функции в качестве аргументов другим функциям
- возвращать функции из других функций
- присваивать функции переменным
- хранить функции в структурах данных
- иметь функции быть анонимными
Лямбда-выражения Java 8 соответствуют всем этим критериям. Так что они кажутся первоклассными.
В статье также упоминаются имена функций, не имеющие особого статуса, вместо этого имя функции представляет собой просто переменную, тип которой является типом функции. Лямбда-выражения Java 8 не соответствуют этому последнему критерию. В Java 8 нет типов функций; он имеет функциональные интерфейсы. Они эффективно используются как функциональные типы, но они вовсе не являются функциональными типами. Если у вас есть ссылка, тип которой является функциональным интерфейсом, вы понятия не имеете, является ли она лямбдой, экземпляром анонимного внутреннего класса или экземпляром конкретного класса, который реализует этот интерфейс.
Таким образом, лямбда-выражения в Java 8 являются более первоклассными функциями, чем я думал изначально. Они просто не являются чистыми первоклассными функциями.
Object o = "Hello";
, литерал Hello String
преобразуется в ссылку типа Object
. С другой стороны, переписывание — это преобразование простого синтаксиса в более сложный синтаксис, который вы могли бы написать сами. Преобразование и переписывание — разные понятия.
- person Stuart Marks; 09.03.2013
(1).toString()
— допустимое выражение), лямбды Java 8 можно было бы считать настоящими гражданами первого класса. Упаковка/распаковка несколько устраняет этот недостаток языка. Конечно, equals() и т. д. должны быть определены и предсказуемы во всем — они могут быть во время выпуска. Хотя я согласен с вашим ни-ни в целом, я думаю, что лямбда-выражения Java гораздо ближе к первоклассному, чем к чистому сахару. А в будущем (Java 9?) они станут еще более «естественными».
- person Alex Pakka; 24.10.2013
<=
и ==
. Но, в целом, в последнее время я также больше думал об этой теме и вскоре обновлю свой ответ.
- person Stuart Marks; 25.10.2013
switch
синтаксическим сахаром для группы if
, хотя любое switch
можно преобразовать в несколько if
. Или рассмотрим ООП: любой класс с методами экземпляра можно переписать в структуроподобный класс со статическими методами, принимающими экземпляр в качестве первого параметра. Это не означает, что ООП — это просто синтаксический сахар: внутренне он очень отличается, и это проявляется в отражении. В любом случае, это вопрос семантики. Мы можем не согласиться с определением синтаксического сахара. Я чувствую, что ваш слишком широк, чтобы быть полезным.
- person Cyrille Ka; 25.10.2013
Function<String,String> fn = s -> s.toUpperCase();
- person Stuart Marks; 21.02.2014
class C { static int f() { return 1; }
, могу ли я передать C.f
насмешнику или что-то в этом роде? Есть ли способ сделать это?
- person sam boosalis; 28.05.2014
IntSupplier
. Затем вы можете передать C::f
всему, что принимает IntSupplier
. На самом деле это не передача ссылки непосредственно на метод f()
, это сахар для лямбды () -> C.f()
, которая вызывает метод f()
. Но это должно быть достаточно близко для большинства целей.
- person Stuart Marks; 29.05.2014
C::f
для создания функционального интерфейса из метода, и это круто. но я все еще не могу издеваться над методами так же легко, как в python. Спасибо!
- person sam boosalis; 29.05.2014
Да, это первоклассные ценности (или будут таковыми после выхода Java 8...)
В том смысле, что вы можете передавать их в качестве аргументов, составлять из них функции более высокого порядка, хранить их в структурах данных и т. д. Вы сможете использовать их для широкого спектра методов функционального программирования.
См. также более подробное определение того, что означает «первый класс» в этом контексте:
На мой взгляд, это синтаксический сахар, но вдобавок к выводу типа, новому пакету java.util.functions
и семантике внутренних классов он выступает как первоклассное значение.
Настоящее замыкание с привязкой переменной к внешнему контексту имеет некоторые накладные расходы. Я бы посчитал реализацию Java 8 оптимальной, достаточно чистой.
По крайней мере, это не просто синтаксический сахар.
И я бы не знал более оптимальной реализации.
Для меня Lambdas в Java 8 — это просто синтаксический сахар, потому что вы не можете использовать его как гражданин первого класса (http://en.wikipedia.org/wiki/First-class_function) каждая функция должна быть заключена в объект, это накладывает много ограничений по сравнению с языком с чисто первоклассными функциями, такими как SCALA. Замыкания Java 8 могут захватывать только неизменяемые («фактически окончательные») нелокальные переменные.
Вот лучшее объяснение, почему это синтаксический сахар Java Lambdas and Closures