Это вторая часть из серии постов в блоге (читай часть I ), в которой приводятся некоторые практические примеры лямбд, как может выглядеть функциональное программирование на Java и как лямбды могут влиять на некоторые из известных библиотек на земле Java. Эта часть посвящена общим методам функционального программирования, которые будут доступны через добавление лямбд. Функциональное программирование (хотя я все еще не считаю Java функциональным языком программирования) сделает код Java более лаконичным, более выразительным и более читабельным в определенных типах проблемных ситуаций.
Примечание. Все следующие примеры работают с текущей реализацией прототипа лямбд.
Упрощение кода
Давайте начнем с простого примера, который показывает, как функции высшего порядка в целом могут привести к упрощенному коду. Предположим, мы хотим найти файлы определенного типа. До сих пор мы, вероятно, использовали бы FileFilter:
Но с функциями более высокого порядка мы могли бы написать метод, который принимает логику фильтрации в качестве аргумента функции. Думайте об этом коде как о части API.
Тогда клиентский код этого API был бы однострочным:
механизмы обратного вызова, такие как фильтрация файлов с помощью FileFilter, как указано выше, обычно можно значительно улучшить, заменив класс обратного вызова функцией. И фактически, обратные вызовы только с одним методом настолько распространены в Java, что будет специальная поддержка для так называемых типов SAM (единственный абстрактный метод, см. Также первую часть этой серии) из проекта lambda. Это позволяет нам вызывать существующий метод File.listFiles (FileFilter) с функцией вместо FileFilter. Поскольку FileFilter является тип SAM, функция будет автоматически преобразована в FileFilter тогда:
Каррирование
Одной из функций, поддерживаемых большинством функциональных языков (некоторые даже используют ее чрезмерно), является каррирование. Карринг позволяет определить функцию с несколькими параметрами и позволить клиенту вызывать ее с меньшим количеством параметров, что будет возвращать функцию, в которой заданные параметры являются фиксированными. Простым примером будет функция, которая принимает два параметра x и y и возвращает сумму. Клиент может вызвать эту функцию с фиксированным значением для x, скажем, 5, но без значения для y. Это приведет к функции, которая принимает один параметр y и возвращает 5 + y. Давайте попробуем реализовать эту функцию в Java:
Здесь мы сначала определяем сумму функций типа ## int (int) (int), то есть функции типа, которая принимает целое число и возвращает функцию типа #int (int). Мы можем оценить это в одном утверждении суммы. (3). (5). Сумма первого вызова. (3) в основном приведет к лямбда-выражению #int (int y) (3 + y). Второй вызов оценит это выражение, передав 5 как значение для y. Но вместо оценки всего выражения мы также можем частично оценить его, как в строке 4. Это приведет к функции add5, которая может быть вызвана с различными значениями y и вернуть 5 + y.
Вот как может выглядеть карри в Java. Согласен, на других языках это выглядит лучше, но в целом это работает. Может быть, еще можно добавить немного синтаксического сахара.
Контрольные абстракции
Еще одной особенностью многих функциональных языков является возможность создания управляющих абстракций, которые выглядят как естественный синтаксис. Это стало возможным благодаря карри и функциям высшего порядка в целом. Давайте посмотрим на простой пример.
afterDelay — функция высшего порядка, она принимает целое число n в качестве аргумента и возвращает функцию, которая принимает функцию f. Возвращенная функция выполнит f через n секунд. Мы можем вызвать его прямо так, как если бы
он вывел «foo» на консоль через две секунды. Или мы можем использовать curry для создания другой функции after5Seconds, которая принимает другую функцию и оценивает ее после ожидания в течение 5 секунд.
Теперь это выглядит как обычный вызов функции, а не как контрольная абстракция. Но сделать так, чтобы это выглядело просто. Просто замените круглые скобки на фигурные скобки,
и это почти похоже на другие управляющие структуры, например for или if.
Теперь давайте посмотрим на другой пример, который демонстрирует
шаблон займа :
Проще говоря, withPrintWriter — это функция, которая принимает файл в качестве аргумента и открывает PrintWriter для этого файла. Затем он вызывает функцию, данную вызывающей стороной с этим PrintWriter (или
одалживает ее функции). Вещи, вероятно, станут намного понятнее, если взглянуть на то, как вызывать withPrintWriter.
Поскольку withWriter — это функция с каррированием, мы можем оценить ее частично (только с помощью файла) и присвоить результат переменной типа функции, которая может вызываться несколько раз.
Обратите внимание, что withWriter также может быть реализован как функция без каррирования. Мы могли бы передать функцию напрямую в качестве второго аргумента.
Этот вид абстракций управления может быть очень полезен для повторного использования кода, например, для удаления стандартного кода при обработке ресурсов (withWriter, withReader, …) или обработке транзакций (inTransaction).
Для удовольствия давайте сделаем еще один пример, который упрощает чтение строк файла по одной:
Опять же, представьте, что каждая строка является частью API, скрытого от клиента. На стороне клиента все становится действительно легко.
Фактически каждая строка должна быть частью самого класса File. И, возможно, он попадет туда в JDK7 через метод расширения (который является темой следующего поста).
Первоначально я не планировал писать так много об управляющих абстракциях (хотя это слишком весело), но вместо этого охватывал рекурсию как функциональную технику.
Рекурсия
К сожалению, до сих пор не совсем ясно , как рекурсивные вызовы в лямбда — выражениях будет выглядеть или , если это будет даже можно писать рекурсивные лямбды (если это не было возможно , мы могли бы еще назвать рекурсивные методы из лямбды). В настоящее время ведутся некоторые дискуссии по лямбда-рассылке проекта, поэтому я пока пропущу эту тему.
Подводя итог, функциональные методы позволяют некоторые упрощения кода в определенных сценариях, как описано выше. Собственно, это и было основной причиной введения лямбда-выражений, а именно, для упрощения кода для новой структуры fork / join и параллельных массивов. Но лямбда-выражения позволяют упростить код и во многих других случаях использования.
От http://stronglytypedblog.blogspot.com/2010/07/lambdas-in-java-preview-part-2.html