Как было объявлено на Devoxx в прошлом году, замыкания (или, лучше, лямбда-выражения) будут (вероятно) добавлены в JDK7. Команда проекта lambda проверила начальные части реализации в репозиториях OpenJDK. Это первая часть (см часть 2) в серии постов в блоге, где приводятся некоторые практические примеры лямбд, как может выглядеть функциональное программирование на Java и как лямбды могут влиять на некоторые из известных библиотек на земле Java. Хотя большинство примеров будут работать с текущей реализацией прототипа, имейте в виду, что это всего лишь предварительный просмотр, основанный на предложении соломенного человека, черновом варианте спецификации, обсуждениях в лямбда-списке рассылки проекта и текущем состоянии. прототипа. Могут быть / будут как семантические, так и синтаксические различия с окончательной версией лямбды в Java. Также некоторые детали опущены, например, обработка исключений, вероятно, будет выходить за рамки.
Основное внимание в этой серии уделено практическому использованию лямбд, а не принципиальным принципам лямбд. Вам не нужно иметь никаких теоретических знаний о лямбда-исчислении или иметь глубокие знания в области функционального программирования. И не думайте о том, что такое замыкания, просто думайте о них как о анонимных функциях или блоках кода, которые можно обойти. Но, несмотря на то, что вам не нужно знать обо всем этом, я не хочу отговаривать вас от изучения этих тем.
Примечание: если вы хотите попробовать некоторые примеры, есть небольшая хитрость, чтобы заставить работать прототип лямбды, смотрите здесь .
Лямбда-выражения
Для начала давайте рассмотрим очень простой пример анонимной функции или «лямбда-выражения». Ниже приведено лямбда-выражение, которое принимает целое число x и возвращает 2 * x:
#(int x)(2*x)
Символ хеша вводит лямбда-выражение, или «литерал функции». Первая пара скобок представляет собой список аргументов, разделенных запятыми, а вторая пара скобок содержит выражение, которое вычисляется при вызове. Вот еще один пример лямбда-выражения, которое принимает два аргумента x и y и возвращает их сумму:
#(int x, int y)(x+y)
Что мы, естественно, сделаем с лямбда-выражением, так это оценим его. Мы могли бы напрямую вызывать один из приведенных выше функциональных литералов напрямую с помощью # (int x) (2 * x). (7), но это, вероятно, будет немного необычно.
Типы функций
Вместо этого вызов в основном происходит с переменными типа функции. Поэтому мы сначала привязываем функцию к переменной и делаем вызов для этого. Синтаксис для типов функций очень похож на синтаксис функциональных литералов: здесь
объявляется переменный удвоитель типа #int (int), то есть функции типа, которая принимает int (в скобках) и возвращает int (после #). Теперь, чтобы вызвать удвоитель, мы можем написать «
Обратите внимание на точку перед скобками». Для другого примера, давайте снова посмотрим на сумму лямбда:
более сложные выражения
До сих пор тело лямбда-выражений было только одним выражением. В этом случае его можно включить в скобки, а ключевое слово return можно опустить. В более сложном случае тело может быть блоком с фигурными скобками и должно явно возвращать значение (если оно не пусто): функции
высшего порядка Конечно, функции также можно передавать в качестве аргументов для методов и других функций. Это будет самое распространенное использование. Функция, которая принимает целое число n и функцию f, которая выполняет fn раз, может выглядеть следующим образом:
Следующее будет выводить квадраты от 0 до 4 на консоли.
Функции и методы также могут иметь функцию в качестве возвращаемого значения. Следующий метод принимает целое число x и возвращает функцию, которая принимает другое целое число y и возвращает x * y.
Вызов выглядит следующим образом:
этот случай также показывает, что можно захватывать переменные из внешней области видимости (это, кстати, делает его закрытием). x — свободная переменная, и ее определение копируется в тело лямбда-выражения во время выполнения. Чтобы это работало, x должен быть объявлен эффективно-окончательным или общим (подробности см. В предложении соломенного человечка).
Вот и все Вот в основном это для основ лямбда-выражения в Java. Но вы, вероятно, уже заметили, что это окажет огромное влияние на Java, как на язык, так и на библиотеки.
Примечание: некоторым людям не нравится синтаксис лямбды, как указано выше. Я не хочу начинать обсуждение здесь снова, только два момента. Во-первых, в некоторых случаях синтаксис может быть довольно неудобным, но большую часть времени он просто передает функции как литералы или переменные, что не выглядит неудобно. И, во-вторых, поскольку Java является языком статической типизации и имеет такие функции, как проверенные исключения и другие, замыкания не будут похожи на языки с динамической типизацией или языки со строгим выводом типа или языки без проверенных исключений.
Преобразование функций
Этот последний раздел посвящен преобразованию функций, которое не является важным для лямбда-выражений, но также окажет огромное влияние. Многие библиотеки Java используют так называемые типы SAM (один абстрактный метод) — интерфейсы только с одним методом, а абстрактные классы — только с одним абстрактным методом. Преобразование функции означает, что функция соответствующего типа может быть преобразована в анонимный экземпляр типа SAM по мере необходимости. Например, метод Collections.sort использует List и Comparator, который имеет единственный абстрактный метод int compare (T x, T y). До сих пор это выглядело бы так:
Но с помощью преобразования функций мы можем заменить функцию соответствующего типа:
Это, вероятно, будет использоваться очень часто, потому что типы SAM так распространены в мире Java. В качестве примечания, у меня открытый вопрос: стоит ли разработчикам API принимать функции в качестве аргументов для своих методов / функций или типов SAM, что может быть более гибким и более выразительным.
Последний пример
Для последнего примера мы реализуем функцию Фибоначчи и вызываем ее несколько раз в параллельных потоках.
fib сначала встречается как переменная класса типа функции. Это лямбда-выражение берет счетчик c и вводит n, вызывает метод fib (я не знаю, возможны ли в настоящий момент рекурсивные лямбда-вызовы), а затем печатает счетчик и результат. Основной метод создает 10 потоков, каждый из которых принимает лямбда-выражение fib, которое неявно преобразуется в Runnable. Вывод примерно такой:
Не стесняйтесь оставлять комментарии о том, что вы думаете о лямбдах прямо здесь, или оставлять отзывы непосредственно членам проекта лямбда, что, я думаю, всегда приветствуется.
От http://stronglytypedblog.blogspot.com/2010/06/lambdas-in-java-preview-part-1-basics.html