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




Самым очевидным было бы псевдозамещение всех тех классов, у которых есть только один метод с именем run () или actionPerformed () или что-то в этом роде. Поэтому вместо создания потока со встроенным Runnable вы должны использовать закрытие. Не мощнее того, что есть сейчас, но гораздо удобнее и лаконичнее.
Так что мы закрываем необходимость? Нет. Было бы хорошо, если бы они были? Конечно, до тех пор, пока они не будут чувствовать себя привязанными, как я боюсь.
Если бы они были такими же полными, как замыкания JavaScript / C# / C++ 0x, они были бы более мощными, чем анонимные внутренние классы, потому что они могли бы захватывать изменяемые ссылки на локальные переменные, а не только копии финалов только для чтения.
@Daniel Earwicker: Вы можете смоделировать это, обернув изменяемую переменную в объект-контейнер (поместив ее в кучу), который затем присваивается локальной конечной переменной.
@Bart van Heukelom - вы действительно можете или даже лучше, вы можете использовать язык, который сделает это за вас! :)
@Bill: Замыкания НЕ то же самое, что анонимные функции или внутренние классы, хотя различия тонкие. См. en.wikipedia.org/wiki/Closure_%28computer_science%29
Вот реализация закрытия для Java 5, 6 и 7 mseifed.blogspot.se/2012/09/… Она содержит все, о чем можно было бы мечтать ... Я думаю, что это довольно круто!
@jodonnell. Я тоже думаю, что это то, что вы описываете. Я не понимаю, почему этот класс никогда не появлялся до сих пор .. mseifed.blogspot.se/2012/09/…
Я полагаю, что для поддержки основных концепций функционального программирования вам нужны замыкания. Делает код более элегантным и компонуемым с поддержкой закрытие. Кроме того, мне нравится идея передавать строки кода в качестве параметров функциям.
Согласен, просто для удобных реализаций карт / фильтров это того стоит. Не говоря уже о большинстве случаев использования анонимных внутренних классов. Важны удобочитаемость и размер кода.
Это первая ссылка, возвращаемая поиском Google для пример закрытия Java
Они не делают объектно-ориентированный язык лучше. Они делают практические языки более практичными.
Если вы решаете проблему с помощью OO-молотка - представляйте все как взаимодействия между объектами - тогда закрытие не имеет смысла. В объектно-ориентированном языке, основанном на классах, замыкания - это задымленные подсобные помещения, где вся работа выполняется, но потом об этом никто не говорит. Концептуально это отвратительно.
На практике это очень удобно. Я действительно не хочу определять новый тип объекта для хранения контекста, устанавливать для него метод «делать что-то», создавать его и заполнять контекст ... я просто хочу сказать компилятору: «посмотри, посмотри, что У меня есть доступ прямо сейчас? Это тот контекст, который я хочу, и вот код, для которого я хочу его использовать - держись за меня, пока он мне не понадобится ».
Фантастический материал.
-1: «Они не делают объектно-ориентированный язык лучше. Они делают практические языки более практичными.»: Я категорически не согласен с этим принципом. Возможно, замыкания сделают Java более практичным, но в принципе не следует жертвовать согласованностью языка только ради того, чтобы сделать его «более практичным». ИМО, должна быть другая, более сильная мотивация для введения закрытий. Язык, который имеет очень разнородную семантику и множество особых случаев, просто для того, чтобы быть «более практичным», труднее выучить. Языки программирования должны основываться на концепциях, а не на идиомах.
В Java были закрытия с момента появления версии 1.1, но это было очень громоздко и ограниченно.
Они часто полезны везде, где есть обратный вызов с некоторым описанием. Обычный случай - абстрагироваться от потока управления, оставляя интересный код для вызова алгоритма с замыканием, не имеющим внешнего потока управления.
Тривиальный пример - для каждого (хотя в Java 1.5 это уже есть). Хотя вы можете реализовать метод forEach в Java в его нынешнем виде, он слишком многословен, чтобы быть полезным.
Примером, который уже имеет смысл для существующей Java, является реализация идиомы «выполнение вокруг», при которой сбор и выпуск ресурсов абстрагируются. Например, открытие и закрытие файла можно выполнить с помощью команды try / finally, без необходимости правильного определения деталей в коде клиента.
Как разработчик java, который пытается научить себя шепелявости в попытке стать лучшим программистом, я бы сказал, что хотел бы, чтобы предложение Джоша Блока о закрытии было реализовано. Я использую анонимные внутренние классы, чтобы выразить такие вещи, как, например, что делать с каждым элементом списка при агрегировании некоторых данных. Было бы неплохо представить это как закрытие, вместо того, чтобы создавать абстрактный класс.
Под «абстрактным классом» вы подразумеваете анонимный внутренний класс?
Я считать, это предложение, о котором вы говорите. docs.google.com/Doc.aspx?id=k73_1ggr36h
да, Том - я действительно это имею в виду - извини И похоже, что Билл - спасибо
Java не нуждается в замыканиях, объектно-ориентированный язык может делать все, что делает замыкание, используя промежуточные объекты для хранения состояния или выполнения действий (в случае Java внутренних классов). Но замыкания желательны как функция, потому что они значительно упрощают код и повышают читаемость и, как следствие, ремонтопригодность кода.
Я не специалист по Java, но использую C# 3.5, и замыкания - одна из моих любимых функций языка, например, возьмем следующий оператор в качестве примера:
// Example #1 with closures
public IList<Customer> GetFilteredCustomerList(string filter) {
//Here a closure is created around the filter parameter
return Customers.Where( c => c.Name.Contains(filter)).ToList();
}
теперь возьмем эквивалентный пример, в котором не используются замыкания.
//Example #2 without closures, using just basic OO techniques
public IList<Customer> GetFilteredCustomerList(string filter) {
return new Customers.Where( new CustomerNameFiltrator(filter));
}
...
public class CustomerNameFiltrator : IFilter<Customer> {
private string _filter;
public CustomerNameFiltrator(string filter) {
_filter = filter;
}
public bool Filter(Customer customer) {
return customer.Name.Contains( _filter);
}
}
Я знаю, что это C#, а не Java, но идея та же, замыкания полезны для краткости и делают код короче и читабельнее. За кулисами замыкания C# 3.5 делают что-то очень похожее на пример № 2, то есть компилятор создает закрытый класс за кулисами и передает ему параметр filter.
Java не нуждается в закрытии для работы, поскольку разработчику они вам тоже не нужны, но они полезны и предоставляют преимущества, а это означает, что они желательны для языка, который является производственным языком, и одна из его целей - продуктивность. .
обратите внимание, что разница намного меньше, когда вы используете анонимные классы
@Michael Borgwardt, обратите внимание, что разница намного больше, когда у вас много замыканий и композиции функций.
Есть несколько очень полезных «функций высшего порядка», которые могут выполнять операции со списками с помощью замыканий. Функции высшего порядка - это функции, имеющие в качестве параметров «функциональные объекты».
Например. очень распространенная операция - применить какое-либо преобразование к каждому элементу в списке. Эта функция более высокого порядка обычно называется «карта» или «сбор». (См. Оператор *. Spread Groovy).
Например, чтобы выровнять каждый элемент в списке без замыканий, вы, вероятно, напишете:
List<Integer> squareInts(List<Integer> is){
List<Integer> result = new ArrayList<Integer>(is.size());
for (Integer i:is)
result.add(i*i);
return result;
}
Используя замыкания, карту и предложенный синтаксис, вы можете написать это так:
is.map({Integer i => i*i})
(Здесь возможна проблема с производительностью, связанная с упаковкой примитивных типов.)
Как объяснил Поп Каталин, существует еще одна функция более высокого порядка, называемая «выбор» или «фильтр»: ее можно использовать для получения всех элементов в списке, соответствующих определенному условию. Например:
Вместо:
void onlyStringsWithMoreThan4Chars(List<String> strings){
List<String> result = new ArrayList<String>(str.size()); // should be enough
for (String str:strings)
if (str.length() > 4) result.add(str);
return result;
}
Вместо этого вы могли бы написать что-нибудь вроде
strings.select({String str => str.length() > 4});
используя предложение.
Вы можете взглянуть на синтаксис Groovy, который является расширением языка Java для поддержки замыканий прямо сейчас. См. Главу о коллекциях Руководство пользователя Groovy для получения дополнительных примеров, что делать с замыканиями.
Замечание:
Возможно, требуется некоторое разъяснение относительно термина «закрытие». То, что я показал выше, строго говоря, без закрытия. Это просто «функциональные объекты». Замыкание - это все, что может захватить - или «закрыть» (лексический) контекст окружающего его кода. В этом смысле сейчас в Java есть замыкания, то есть анонимные классы:
Runnable createStringPrintingRunnable(final String str){
return new Runnable(){
public void run(){
System.out.println(str); // this accesses a variable from an outer scope
}
};
}
Проголосовали за знание (и объяснение) различия между замыканиями и функциями как значениями
Когда в Java наконец появятся замыкания, я с радостью избавлюсь от всех своих пользовательских классов компаратора.
myArray.sort( (a, b) => a.myProperty().compareTo(b.myProperty() );
... выглядит чертовски лучше, чем ...
myArray.sort(new Comparator<MyClass>() {
public int compare(MyClass a, MyClass b) {
return a.myProperty().compareTo(b.myProperty();
}
});
Не только этот бенджисмит, но мне нравится, как вы можете просто делать ...
myArray.sort {it.myProperty}
Вам нужен только более подробный компаратор, который вы показали, когда сравнение свойства на естественном языке не соответствует вашим потребностям.
Мне очень нравится эта функция.
I've been reading a lot lately about the next release of Java possibly supporting closures. I feel like I have a pretty firm grasp on what closures are, but I can't think of a solid example of how they would make an Object-Oriented language "better."
Что ж, большинство людей, которые используют термин «закрытие», на самом деле имеют в виду «объект функции», и в этом смысле объекты функции позволяют писать более простой код в определенных обстоятельствах, например, когда вам нужны настраиваемые компараторы в функции сортировки.
Например, в Python:
def reversecmp(x, y):
return y - x
a = [4, 2, 5, 9, 11]
a.sort(cmp=reversecmp)
Это сортирует список в обратном порядке, передавая пользовательскую функцию сравнения reversecmp. Добавление лямбда-оператора делает вещи еще более компактными:
a = [4, 2, 5, 9, 11]
a.sort(cmp=lambda x, y : y - x)
В Java нет функциональных объектов, поэтому для их моделирования используются «классы функторов». В Java вы выполняете аналогичную операцию, реализуя пользовательскую версию класса Comparator и передавая ее функции сортировки:
class ReverseComparator implements Comparator {
public compare(Object x, Object y) {
return (Integer) y - (Integer) x;
}
...
List<Integer> a = Arrays.asList(4, 2, 5, 9, 11);
Collections.sort(a, new ReverseComparator());
Как видите, он дает тот же эффект, что и закрытие, но более неуклюжий и многословный. Однако добавление анонимных внутренних классов устраняет большую часть боли:
List<Integer> a = Arrays.asList(4, 2, 5, 9, 11);
Comparator reverse = new Comparator() {
public Compare(Object x, Object y) {
return (Integer) y - (Integer) x;
}
}
Collections.sort(a, reverse);
Поэтому я бы сказал, что комбинации классов функторов + анонимных внутренних классов в Java достаточно, чтобы компенсировать отсутствие настоящих функциональных объектов, делая их добавление ненужным.
Как программист на Лиспе я хотел бы, чтобы сообщество Java понимало следующую разницу: функции как объекты против замыканий.
а) функции могут быть именованными или анонимными. Но они также могут быть объектами самих себя. Это позволяет передавать функции как аргументы, возвращать их из функций или сохранять в структурах данных. Это означает, что функции являются объектами первого класса на языке программирования.
Анонимные функции мало что добавляют к языку, они просто позволяют вам писать функции более коротким способом.
б) Замыкание - это функция плюс среда привязки.. Замыкания могут передаваться вниз (как параметры) или возвращаться вверх (как возвращаемые значения). Это позволяет функции обращаться к переменным своего окружения, даже если окружающий код больше не активен.
Если у вас есть а) на каком-то языке, то возникает вопрос, что делать с б)? Есть языки, у которых есть а), но не б). В мире функционального программирования а) (функции) и б (функции как замыкания) в настоящее время являются нормой. Smalltalk долгое время имел а) (блоки - анонимные функции), но затем некоторые диалекты Smalltalk добавили поддержку б) (блоки как закрытие).
Вы можете представить, что получите немного другую модель программирования, если добавите в язык функции и замыкания.
С прагматической точки зрения анонимная функция добавляет некоторые короткие обозначения, в которых вы передаете или вызываете функции. Это может быть хорошо.
Замыкание (функция плюс привязка) позволяет, например, создать функцию, которая имеет доступ к некоторым переменным (например, к значению счетчика). Теперь вы можете сохранить эту функцию в объекте, получить к ней доступ и вызвать ее. Контекст для объекта функции теперь - это не только объекты, к которым у него есть доступ, но и переменные, к которым он имеет доступ через привязки. Это также полезно, но вы можете видеть, что привязки переменных вместо доступа к объектным переменным теперь представляют собой проблему: когда должно быть что-то, переменная лексический (к которой можно получить доступ в замыкании), а когда это должна быть переменная какого-то объекта ( а слот). Когда что-то должно быть закрытием или объектом? Вы можете использовать оба одинаковыми способами. Обычное упражнение по программированию для студентов, изучающих Scheme (диалект Лиспа), - это написать простую объектную систему с использованием замыканий.
В результате получается более сложный язык программирования и более сложная модель времени выполнения. Слишком сложно?
+2, если бы я мог. Это «недостающее звено» в дискуссиях о закрытии Java. Заслуживает самостоятельного вопроса, Райнер.
Это лучшее описание замыкания, с которым я столкнулся. Теперь я понимаю, почему новая реализация закрытия ограничена только локальной. Опасно Уилл Робинсон, Опасно!
+1 Теперь я понимаю, почему Джошуа Блох говорит, что в Java уже есть замыкания (в виде анонимных внутренних классов). Прочитав то, что вы сказали, я согласен. Проблемы с a.i.c в том, что они многословны.
Как обеспечить безопасность типов с помощью замыкания? Удивительно абстрактный анонимный класс понятнее замыкания?
Замыкания в императивном языке (примеры: JavaScript, C#, предстоящее обновление C++) - это не то же самое, что анонимные внутренние классы. Они должны иметь возможность фиксировать изменяемые ссылки на локальные переменные. Внутренние классы Java могут захватывать только локальные переменные final.
Практически любую языковую функцию можно критиковать как несущественную:
for, while, do - это просто синтаксический сахар по сравнению с goto / if.Точно такой же «несущественный» аргумент должен был заблокировать включение всех вышеперечисленных функций.
Чтобы быть ясным, мой вопрос не был предназначен для аргументации в пользу блокировки включения закрытий. Кроме того, я бы согласился с аргументом о «несущественности» одной из упомянутых вами выше характеристик. :)
Многие функции не важны. Однако, IMHO, следует посмотреть, как часто функция будет использоваться по сравнению со сложностью, которую она добавляет к семантике и инструментам языка.
Некоторые люди сказали или подразумевали, что замыкания - это просто синтаксический сахар - они делают то, что вы уже могли делать с анонимными внутренними классами, и делают более удобной передачу параметров.
Они являются синтаксическим сахаром находятся в том же смысле, что и Java - синтаксическим сахаром для ассемблера (этот «ассемблер» может быть байт-кодом для аргументации). Другими словами, они повышают уровень абстракции, и это важная концепция.
Замыкания продвигают концепцию функции как объекта к первоклассной сущности - той, которая увеличивает выразительность кода, а не загромождает его еще большим количеством шаблонов.
Пример, который мне близок, уже упоминался Томом Хотином - реализация идиомы Execute Around, которая является чуть ли не единственным способом перенести RAII в Java. Я написал в блоге запись именно на эту тему пару лет назад, когда я впервые услышал, что могут быть закрыты.
По иронии судьбы, я думаю, что сама причина того, что замыкания были бы полезны для Java (больше выразительности при меньшем количестве кода), может быть тем, что беспокоит многих сторонников Java. У Java есть мышление «подробно изложить все». Это и тот факт, что замыкания - это намек в сторону более функционального способа работы - что я также считаю хорошей вещью, но может смягчить чистое объектно-ориентированное послание, которое многие в сообществе Java ценят.
Я думаю, что «изложить все подробно» было бы точнее, чем «изложить все подробно» (по крайней мере, в моем случае). Есть небольшая разница.
Что ж, я бы сказал, что это два разных случая с большим пересечением. Так что, возможно, если мы поговорим о союзе этих двоих, мы дойдем до сути :-)
Функции первого класса - это отличная идея от замыканий. В первом случае ссылка на функцию (или саму функцию) рассматривается как значение - значение, которое может быть передано и вызвано по желанию. Последнее связано с захватом ссылок на значения, доступными лексически в точке определения, и продлением их времени жизни на время жизни созданной там функции. Можно иметь первоклассные функции без замыканий, хотя я не думаю, что обратное имеет смысл.
Что ж, это разные идеи, но одна основана на другой (отсюда отсутствие транзитивности в вашем последнем комментарии). Я сказал: «Замыкает продвигать концепцию функции как объекта сущности первого класса», но это не то же самое. Дело в том, что в нынешнем виде Java не имеет надлежащей концепции функций первого класса, но если бы она получила закрытие, она бы тоже получила это в качестве предварительного условия. Я действительно это имел в виду. Но если это было непонятно, хорошо, что вы разъяснили это своим комментарием :-)
Повышая уровень абстракции, вы вводите новую семантику, а не просто используете синтаксический сахар. Java не является синтаксическим сахаром над сборкой.
Что насчет удобочитаемости и ремонтопригодности ... однострочные закрытия сложнее понять и отладить, имо Программное обеспечение имеет долгую жизнь, и вы можете заставить людей с элементарными знаниями языка поддерживать его ... Так что лучше распределяйте логику, чем однострочные, для легкого обслуживания ... релиз...
Вы говорите исходя из своего опыта? Мой опыт был прямо противоположным.
Возможно, вы захотите взглянуть на Groovy, язык, который в основном совместим с Java и работает на JRE, но поддерживает замыкания.
Я много думал над темой этого очень интересный вопрос в последние несколько дней. Во-первых, если я правильно понял, в Java уже есть некоторое базовое понятие замыканий (определенное через анонимные классы), но новая функция Будет добавлена поддержка замыканий на основе анонимных функций.
Это расширение определенно сделает язык более выразительным, но я не уверен если это действительно соответствует остальному языку. Java была разработана как объектно-ориентированный язык без поддержки функционального программирования: будет ли новая семантика легкой для понимания? В Java 6 даже нет функций, будут ли в Java 7 анонимные функции, но не «нормальные» функции?
У меня сложилось впечатление, что новые стили или парадигмы программирования, такие как функциональные программирования становятся все более популярными, все хотят использовать их в своих любимый язык ООП. Это и понятно: хочется продолжать употреблять язык, с которым они знакомы, пока внедряют новые функции. Но так язык может стать действительно сложным и потерять связность.
Поэтому сейчас я придерживаюсь Java 6 для ООП (я надеюсь, что Java 6 все еще будет будет поддерживаться какое-то время) и, если мне действительно интересно заниматься ООП + ФП, взглянуть на какой-нибудь другой язык, например Scala (Scala был определен как мульти- парадигма с самого начала и может быть хорошо интегрирована с Java) вместо переключения в Java 7.
Я думаю, что Java обязана своим успехом тому факту, что она сочетает в себе простой язык с очень мощные библиотеки и инструменты, и я не думаю, что новые функции, такие как закрытие, будут сделать его лучшим языком программирования.
Это не ответ на вопрос, но +1 за ваши предложения! Лично я делать использую Scala, и его функциональное программирование великолепно.
Спасибо за предложение и голосование. Может быть, мне следовало сделать мое заявление более явным: я думаю, что Java не требует замыканий и что это расширение сделает язык излишне сложным.
Думаю, в этом вы правы. Я думаю, что у Java никогда не было функций как объектов, потому что Sun не хотела, чтобы они были похожи на указатели функций C (по крайней мере, я читал в Core Java 2). Однако существуют способы реализации функций как объектов находятся, которые не включают указатели на функции. И снова Scala - лучший тому пример.
Я только что купил свою книгу Scala, стал ее читать, и она мне очень нравится! Определенно, я думаю, что не следует возиться с такими языками, как Java или C++, которые не были разработаны для поддержки FP. Java остается одним из моих любимых языков, но если кто-то хочет ООП + ФП, следует выбрать язык, который был разработан для этого.
Теперь, когда JDK8 скоро будет выпущен, доступна дополнительная информация, которая может обогатить ответы на этот вопрос.
Бриа Гетц, языковый архитектор в Oracle, опубликовала серию статей (пока еще черновиков) о текущем состоянии лямбда-выражений в Java. Это также касается закрытия, поскольку они планируют выпустить их в предстоящем JDK, который должен иметь полный код примерно в январе 2013 г. и должен быть выпущен примерно в середине 2013 года.
Lexical scoping и Variable Capture from the State of Lambda - отличное чтение; но «эффективный финал», ИМХО, не такая уж хорошая идея, компилятору, вероятно, понадобится дополнительный проход, чтобы определить «эффективную завершенность», а также код, который компилируется сегодня, начинает ломаться завтра, потому что недавно закодированная строка «в милях» от закрытия преобразовал ссылку в изменяемую.
@UstamanSangat Насколько я понимаю, все свободные переменные в замыкании в Java фактически являются окончательными вне зависимости от того, объявляете вы их явно или нет. Я давно не читал стратегию перевода, поэтому я не знаю, является ли это просто компилятором или каким-то образом встроен в JVM.
Я согласен, моя точка зрения была в основном о новой строке кода в 20 строках от закрытия, изменяющей ранее эффективную конечную переменную на изменяемую. Когда этот код не компилируется, менее искушенный разработчик может запутаться. Но я думаю, что IDE всегда может помочь.
Отсутствие привязки в анонимной функции [т.е. если переменные (и аргументы метода, если есть включающий метод) внешнего контекста объявлены окончательный, тогда они доступны, но не в противном случае], я не совсем понимаю, что это ограничение фактически покупает.
Я все равно обильно использую слово «финал». Итак, если бы я намеревался использовать одни и те же объекты внутри замыкания, я бы действительно объявил эти объекты окончательными в охватывающей области. Но что было бы неправильным в том, чтобы позволить «закрытию [java a.i.c.]» просто получить копию ссылки, как если бы она была передана через конструктор (ну, на самом деле это делается именно так).
Если замыкание хочет перезаписать ссылку, пусть будет так; он будет делать это, не изменяя копию, которую видит охватывающая область видимости.
Если мы утверждаем, что это приведет к нечитаемому коду (например, может быть, непросто увидеть, какая ссылка на объект находится во время вызова конструктора для a.i.c.), то как насчет того, чтобы хотя бы сделать синтаксис менее подробным? Скала? Groovy?
Ссылки @Edwin Dolarzo приятно читать. Необходимо знать, какой подход уже используется в Scala, Groovy и других.
Спасибо за усиление моего подозрения, что замыкания будут просто синтаксическим сахаром для анонимных внутренних классов.