Java 8 танцует вокруг функций первоклассных граждан?

Таким образом, функциональному программисту во мне нравятся такие языки, как Python, которые рассматривают функции как граждан первого класса. Похоже, что Java 8 уступила давлению и «вроде» реализовала такие вещи, как лямбда-выражения и ссылки на методы.

Мой вопрос в том, находится ли Java на пути к использованию функций первого класса, или это действительно просто синтаксический сахар, чтобы уменьшить количество кода, необходимого для реализации анонимных классов/интерфейсов, таких как runnable? (мое нутро говорит последнее).

Мой идеальный сценарий

Map<String,DoubleToDoubleFunc> mymap = new HashMap<String,DoubleToDoubleFunc>();
...
mymap.put("Func 1", (double a, double b) -> a + b);
mymap.put("Func 2", Math::pow);
...
w = mymap.get("Func 1")(y,z);
x = mymap.get("Func 2")(y,z);

person C.B.    schedule 26.03.2014    source источник
comment
Это не синтаксический сахар для анонимных классов; то, во что он компилируется, несколько сложнее.   -  person Louis Wasserman    schedule 26.03.2014
comment
См. мой ответ о том, являются ли лямбды первоклассными: stackoverflow.com/a/15241404/1441122   -  person Stuart Marks    schedule 26.03.2014
comment
См. этот интересный ответ Брайана Гетца в список рассылки лямбда.   -  person Edwin Dalorzo    schedule 27.03.2014


Ответы (4)


В Java 8 нет структурного типа function. Но в Java всегда были объекты первого класса. И фактически они использовались в качестве альтернативы. Исторически сложилось так, что из-за отсутствия первоклассных функций до сих пор мы просто помещали функцию внутрь объекта. Это известные типы SAM (одиночные абстрактные типы методов).

Итак, вместо

function run() {}
Thread t = new Thread(run);

We do

Runnable run = new Runnable(){ public void run(){} };
Thread t = new Thread(run);

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

JDK 8 просто упрощает реализацию этой концепции, и они называют этот тип интерфейсов-оболочек «функциональными интерфейсами» и предлагают некоторый синтаксический сахар для реализации объектов-оболочек.

Runnable run = () -> {};
Thread t = new Thread(run);

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

В списке рассылки lambda Брайан Гетц дал хорошее объяснение некоторых причин, которые мотивировали этот дизайн.

В соответствии с тем, что мы обсуждали сегодня, вот быстрый взгляд на то, куда мы движемся. Мы исследовали путь «возможно, лямбда-выражения должны быть просто экземплярами внутреннего класса, это было бы очень просто», но в итоге пришли к позиции «функции — лучшее направление для будущего языка».

Это исследование проходило поэтапно: сначала внутри группы до формирования ЭГ, а затем еще раз, когда ЭГ обсуждала проблемы. Ниже представлена ​​моя позиция по этому вопросу. Надеюсь, это заполнит некоторые пробелы между тем, что сейчас говорится в спецификации, и тем, что мы говорим об этом.

Возникшие вопросы о том, являются ли лямбда-выражения объектами или нет, в значительной степени сводятся к философским вопросам, таким как «что такое лямбда-выражения на самом деле», «почему Java выиграет от лямбда-выражений» и, в конечном счете, «как лучше развивать язык и платформу Java».

Позиция Oracle заключается в том, что Java должна развиваться — разумеется, осторожно — чтобы оставаться конкурентоспособной. Это, конечно, сложная балансировка.

Я считаю, что лучшим направлением развития Java является поощрение более функционального стиля программирования. Роль Lambda в первую очередь заключается в поддержке разработки и использования более функциональных библиотек; Я предложил такие примеры, как filter-map-reduce, чтобы проиллюстрировать это направление.

В экосистеме имеется множество свидетельств, подтверждающих гипотезу о том, что при наличии инструментов, позволяющих легко это сделать, объектно-ориентированные программисты готовы использовать функциональные методы (такие как неизменность) и превращать их в объектно-ориентированное представление о мире. , и в результате будет писать более качественный, менее подверженный ошибкам код. Проще говоря, мы считаем, что лучшее, что мы можем сделать для Java-разработчиков, — это мягко подтолкнуть их к более функциональному стилю программирования. Мы не собираемся превращать Java в Haskell или даже в Scala. Но направление понятно.

Lambda — это первый взнос за эту эволюцию, но это далеко не конец истории. Остальная часть истории еще не написана, но сохранение наших вариантов является ключевым аспектом того, как мы оцениваем решения, которые принимаем здесь.

Вот почему я так упорно настаивал на том, что лямбда-выражения не являются объектами. Я считаю, что позиция «лямбды — это просто объекты», хотя и очень удобная и заманчивая, захлопывает дверь в ряде потенциально полезных направлений эволюции языка.

В качестве одного примера возьмем типы функций. Лямбда-соломинка, предлагаемая на devoxx, имела типы функций. Я настоял, чтобы мы их удалили, и это сделало меня непопулярным. Но мое возражение против функциональных типов заключалось не в том, что я не люблю функциональные типы — я люблю функциональные типы, — а в том, что функциональные типы плохо боролись с существующим аспектом системы типов Java — стиранием. Типы стертых функций — худшее из обоих миров. Поэтому мы убрали это из дизайна.

Но я не хочу говорить, что «в Java никогда не будет типов функций» (хотя я понимаю, что в Java может никогда не быть типов функций). Я считаю, что для того, чтобы добраться до типов функций, мы должны сначала иметь дело со стиранием. Это может быть, а может быть и невозможно. Но в мире материализованных структурных типов функциональные типы приобретают гораздо больший смысл.

Мировоззрение лямбда-объектов противоречит этому возможному будущему. Взгляд на мир как лямбда-функция не работает, и сохранение этой гибкости является одним из аргументов в пользу того, чтобы не обременять лямбда-выражения даже видимостью объектности.

Вы можете подумать, что текущий дизайн тесно связан с блоком объектов для лямбда-выражений — типов SAM — что делает их в любом случае эффективными объектами. Но это было тщательно скрыто от поверхности, чтобы мы могли в будущем рассмотреть «голые» лямбда-выражения, или рассмотреть другие контексты преобразования для лямбда-выражений, или более тесно интегрировать лямбда-выражения в управляющие конструкции. Мы не делаем этого сейчас, и у нас даже нет конкретного плана для этого, но возможность сделать это в будущем является важной частью дизайна.

Я с оптимизмом смотрю в будущее Java, но чтобы двигаться вперед, нам иногда приходится отказываться от некоторых удобных идей. Лямбда-функции открывают двери. Lambdas-are-objects закрывает их. Мы предпочитаем, чтобы эти двери оставались открытыми.

person Edwin Dalorzo    schedule 26.03.2014
comment
Спасибо, что процитировали письмо от Брайана. Отличное чтение! - person asgs; 08.08.2016
comment
Я должен что-то упустить. Примеры показывают, что лямбда-выражения компилируются в объекты. Тем не менее, Брайан говорит: «Хорошо, что мы сделали лямбда-выражения как функции, а не лямбды-как-объекты». Итак, если вышеприведенное — это лямбда-функции, может ли кто-нибудь показать, как может выглядеть код лямбда-как-объектов (отброшенный подход)? - person joeytwiddle; 11.08.2016
comment
@joeytwiddle Я думаю, что Брайан, вероятно, говорит об инструментарии лямбда-выражений на уровне JVM. Когда он говорит лямбда-выражения как объекты, он, вероятно, имеет в виду инструментирование лямбда-выражений как внутренних классов. Если бы они сделали это, они, вероятно, сильно затруднили бы поддержку реальных типов функций в будущем. Однако они не сделали этого для инструментальных лямбда-выражений, они использовали invokeDynamic и несколько других приемов и это реализовано таким образом, что если JVM когда-либо поддерживает типы структурных функций, их решение также может быть адаптировано к этому. - person Edwin Dalorzo; 11.08.2016

На самом деле это не первоклассные функции IMO. Lambda в конечном итоге является экземпляром функционального интерфейса. Но это дает вам преимущества первоклассной функции. Вы можете передать его в качестве аргумента методу, который ожидает экземпляр функционального интерфейса. Вы можете присвоить его переменной, и в этом случае ее тип будет выведен с помощью целевой типизации. Вы можете вернуть его из метода и так далее.

Кроме того, лямбды не полностью заменяют анонимные классы. Не все анонимные классы можно преобразовать в лямбда-выражения. Вы можете просмотреть этот ответ, чтобы получить хорошее объяснение различий между ними. Итак, никакие лямбды не являются синтаксическим сахаром для анонимных классов.

person Rohit Jain    schedule 26.03.2014
comment
Они действительно объекты, не исключающие друг друга, во многих языках функции также являются объектами. - person Benjamin Gruenbaum; 26.03.2014

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

См. http://cr.openjdk.java.net/~briangoetz/lambda/lambda-translation.html для получения полной информации.

person Louis Wasserman    schedule 26.03.2014
comment
Реализация invokedynamic кажется довольно интересной, поскольку рассматривает конструкцию функционального интерфейса как API. Я бы подумал, что это приведет к тому, что оценка во время выполнения вызовет проблемы с производительностью, но я думаю, мне придется продолжать читать, почему они не возникают. :) - person C.B.; 26.03.2014

interface DoubleDoubleToDoubleFunc {
    double f(double x, double y);
}

public static void main(String[] args) {
    Map<String, DoubleDoubleToDoubleFunc> mymap = new HashMap<>();
    mymap.put("Func 1", (double a, double b) -> a + b);
    mymap.put("Func 2", Math::pow);
    double y = 2.0;
    double z = 3.0;
    double w = mymap.get("Func 1").f(y,z);
    double v = mymap.get("Func 2").f(y,z);
}

Так что это все еще синтаксический сахар, и только до некоторой степени.

person Joop Eggen    schedule 26.03.2014