Закрытие в Java - синтаксические различия между тремя основными предложениями?

Были представлены три основных предложения по добавлению закрытия на язык Java:

Мой вопрос:

  • Каковы различия между тремя предложениями (BGGA, CICE и FCM) в терминах синтаксиса?

Ответ 1

Этот документ IBM дает хорошие примеры разницы синтаксиса между BGGA и CICE:

Предложение BGGA

Предложение BGGA создает концепцию типов функций, где функция имеет список типизированных аргументов, тип возвращаемого значения и предложение throw. В предложении BGGA код суммы квадратов будет выглядеть как код в листинге 9:

Листинг 9. Вычисление суммы квадратов с использованием синтаксиса закрытия BGGA

sumOfSquares = mapReduce(myBigCollection, 
                         { Double x => x * x }, 
                         { Double x, Double y => x + y });

Код внутри скобок слева от символа = > идентифицирует имена и типы аргументов; код справа представляет собой реализацию анонимной функции. Этот код может ссылаться на локальные переменные, определенные в блоке, аргументы закрытия или переменные из области, в которой создается замыкание.

В предложении BGGA вы можете объявлять переменные, аргументы метода и возвращаемые значения метода, которые являются типами функций. Вы можете предоставить закрытие в любом контексте, где ожидается экземпляр одного абстрактного класса метода (например, Runnable или Callable); для анонимных типизированных закрытий предусмотрен метод invoke(), поэтому вы можете вызвать их с указанным списком аргументов.

Одна из основных целей предложения BGGA - дать возможность программистам создавать методы, которые действуют как структуры управления. Соответственно, BGGA также предлагает некоторый синтаксический сахар, чтобы вы могли вызывать методы, которые допускают закрытие, как если бы они были новыми ключевыми словами, чтобы вы могли создавать такие методы, как withLock() или forEach(), и вызывать их, как если бы они были управляющими примитивами. В листинге 10 показано, как метод withLock() будет определен в предложении BGGA; В листинге 11 и листинге 12 показано, как он будет вызываться, используя как стандартную форму, так и форму "контрольная конструкция":

Листинг 10. Кодирование метода withLock() в рамках предложения закрытия BGGA

public static <T,throws E extends Exception>
T withLock(Lock lock, {=>T throws E} block) throws E {
    lock.lock();
    try {
        return block.invoke();
    } finally {
        lock.unlock();
    }
}

Метод withLock() в листинге 10 принимает блокировку и закрытие. Возвращаемый тип и предложение throw замыкания являются общими аргументами; тип вывода в компиляторе обычно позволяет его вызывать без указания значения T и E, как в листинге 11 и листинге 12:

Листинг 11. Вызов withLock()

withLock(lock, {=>
    System.out.println("hello");
});

Листинг 12. Вызов withLock() с использованием сокращенной конструкции управления

withLock(lock) {
    System.out.println("hello");
}

Подобно дженерикам, большая часть сложности закрытия в предложении BGGA несет разработчики библиотеки; использование библиотечных методов, которые допускают замыкания, намного проще.

Предложение BGGA также работает для исправления ряда сбоев прозрачности, которые присутствуют при попытке использовать внутренние экземпляры классов для получения преимуществ закрытия. Например, семантика return, break и this различаются в блоке кода, чем в Runnable (или другом внутреннем экземпляре класса), который представляет один и тот же блок кода. Эти элементы непрозрачности могут вызвать путаницу при переносе кода, чтобы воспользоваться универсальными алгоритмами.

Предложение CICE

Предложение CICE - это более простое предложение, в котором рассматривается проблема, заключающаяся в том, что создание экземпляров внутренних классов слишком громоздко. Вместо создания понятия типов функций он просто создает более компактный синтаксис для создания экземпляров внутреннего класса с помощью одного абстрактного метода (например, Runnable, Callable или Comparator).

В листинге 13 показано, как будет выглядеть сумма кода квадратов в CICE. В нем четко указаны типы UnaryFunction и BinaryFunction, используемые mapReduce(). Аргументами mapReduce() являются анонимные классы, полученные из UnaryFunction и BinaryFunction; синтаксис просто исключает большую часть избыточности, связанной с созданием анонимного экземпляра.

Листинг 13. Сумма квадратов кода в рамках предложения закрытия CICE

Double sumOfSquares = mapReduce(myBigCollection,
    UnaryFunction<Double>(Double x) { return x*x; },
    BinaryFunction<Double, Double>(Double x, Double y) { return x+y; });

Поскольку объекты, представляющие функции, переданные в mapReduce(), являются обычными экземплярами анонимного класса, их тела могут ссылаться на переменные, определенные в охватывающей области; единственная разница между подходами в листинге 13 и листинге 7 - многословность синтаксиса.