Подтвердить что ты не робот

Требуется ли Java закрытие?

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

4b9b3361

Ответ 1

Как программист Lisp, я хотел бы, чтобы сообщество Java понимало следующее различие: функционирует как объекты против закрытия.

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

Анонимные функции мало добавляют к языку, они просто позволяют вам писать функции короче.

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

Если у вас есть a) на каком-то языке, возникает вопрос, что делать с b)? Существуют языки с a), но не b). В мире функционального программирования a) (функции) и b (функции закрытия) в настоящее время является нормой. Smalltalk долгое время a) (блоки анонимные функции), но затем некоторые диалекты Smalltalk добавили поддержку b) (блокируют как закрытие).

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

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

Закрытие (функция плюс привязка) позволяет вам, например, создать функцию, которая имеет доступ к некоторым переменным (например, к значению счетчика). Теперь вы можете сохранить эту функцию в объекте, получить к нему доступ и вызвать его. Контекст для объекта функции теперь - это не только объекты, к которым он имеет доступ, но также переменные, к которым он имеет доступ, посредством привязок. Это также полезно, но вы можете видеть, что переменные привязки или доступ к объектным переменным теперь являются проблемой: когда должна быть что-то лексическая переменная (к которой можно получить доступ в закрытии) и когда она должна быть переменной какого-либо объекта ( Слот). Когда что-то должно быть закрытием или объектом? Вы можете использовать оба метода аналогичным образом. Обычное упражнение по программированию для учащихся, изучающих схему (диалект Lisp), состоит в том, чтобы написать простую объектную систему, используя закрытие.

В результате получается более сложный язык программирования и более сложная модель времени исполнения. Слишком сложно?

Ответ 2

Они не улучшают объектно-ориентированный язык. Они делают практические языки более практичными.

Если вы атакуете проблему с молотом OO - представляете все как взаимодействие между объектами - тогда закрытие не имеет смысла. На классном языке OO закрытие - это заполненные дымом комнаты, где делаются вещи, но об этом никто не говорит. Концептуально это отвратительно.

На практике это очень удобно. Я действительно не хочу определять новый тип объекта для хранения контекста, устанавливать для него метод "делать вещи", создавать его и заполнять контекст... я просто хочу сказать компилятору: "Посмотрите, посмотрите, что У меня есть доступ прямо сейчас? В этом контексте я хочу, и вот код, который я хочу использовать для него, - держись за это для меня, пока мне это не понадобится".

Фантастический материал.

Ответ 3

Наиболее очевидной будет псевдозамена для всех тех классов, у которых есть только один метод под названием run() или actionPerformed() или что-то в этом роде. Поэтому вместо создания Thread с внедренным Runnable вместо этого вы используете закрытие. Не более мощный, чем у нас сейчас, но гораздо более удобный и лаконичный.

Итак, нужны ли нам закрытия? Нет. Было бы неплохо иметь? Конечно, до тех пор, пока они не чувствуют себя болтами, как я боюсь, они были бы.

Ответ 4

Я полагаю, что для поддержки концепций функционального программирования ядра вам нужны замыкания. Делает код более элегантным и композитным с поддержкой закрытия. Кроме того, мне нравится идея пропускать строки кода в качестве параметров для функций.

Ответ 5

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

например. это очень распространенная операция для применения некоторого преобразования к каждому элементу в списке. Эта функция более высокого порядка обычно называется "картой" или "собирать". (См. Оператор *. 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})

(Здесь существует возможная проблема с производительностью в отношении бокса примитивных типов.)

Как объясняется Pop Catalin, есть еще одна функция более высокого порядка, называемая "select" или "filter": ее можно использовать для получения всех элементов в списке, соответствующих некоторому условию. Например:

Вместо:

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
       }
    };
}

Ответ 6

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

Я не специалист по Java, но я использую С# 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);
    }
}

Я знаю, что это С#, а не Java, но идея такая же, закрытие полезно для краткости и делает код короче и читабельнее. За кулисами закрытие С# 3.5 делает что-то похожее на пример # 2, что означает, что компилятор создает закрытый класс за кулисами и передает ему параметр "filter".

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

Ответ 7

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

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

Например, в Python:

def reversecmp(x, y):
   return y - x

a = [4, 2, 5, 9, 11]
a.sort(cmp=reversecmp)

Сортирует список a в обратном порядке, передавая произвольное сравнение functoin 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 достаточно, чтобы компенсировать отсутствие истинных объектов функции, что делает их добавление ненужным.

Ответ 8

Java имеет закрытие с 1.1, просто очень громоздким и ограниченным способом.

Они часто полезны везде, где у вас есть обратный вызов какого-либо описания. Частным случаем является абстрагирование потока управления, оставляя интересный код для вызова алгоритма с закрытием, которое не имеет внешнего потока управления.

Тривиальный пример для каждого (хотя Java 1.5 уже имеет это). Хотя вы можете внедрить метод forEach в Java в том виде, в котором он стоит, он слишком подробен, чтобы быть полезным.

Примером, который уже имеет смысл с существующей Java, является реализация икону "выполнить вокруг", в результате чего сбор и выпуск ресурсов абстрагируются. Например, открывать и закрывать файлы можно в try/finally, без того, чтобы код клиента должен был правильно получить информацию.

Ответ 9

Когда закрытие, наконец, прибудет в 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();
   }
});

Ответ 10

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

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

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

Пример, близкий моему сердцу, уже упоминался Томом Хоутином - реализация идиомы Execute Around, которая представляет собой единственный способ получить RAII на Java. Я написал запись в блоге на эту тему пару лет назад, когда я впервые услышал, что закрытие может появиться.

По иронии судьбы, я думаю, что причина, по которой закрытие будет хорошо для Java (более выразительная с меньшим количеством кода), может быть тем, что гремит многих адвокатов Java. У Java есть менталитет "заклинания все из длинного пути". Это и тот факт, что закрытие - это кивка в сторону более функционального способа делать вещи, которые я также вижу как хорошая вещь, но могу опустить чистое сообщение OO, которое многие в сообществе Java поддерживают.

Ответ 11

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

Это расширение, безусловно, сделает язык более выразительным, но я не уверен если он действительно подходит для остальной части языка. Java был разработан как объектно-ориентированный язык без поддержки функционального программирования: будет ли понятна новая семантика? Java 6 даже не имеет функций, будет ли у Java 7 анонимные функции, но нет "нормальных" функций?

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

Итак, мое отношение на данный момент заключается в том, чтобы придерживаться Java 6 для OOP (надеюсь, что Java 6 будет по-прежнему поддерживаться некоторое время), и, если я действительно заинтересован в выполнении OOP + FP, чтобы посмотреть на какой-то другой язык, такой как Scala (Scala парадигма с самого начала и может быть хорошо интегрирована с Java) вместо переключения к Java 7.

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

Ответ 12

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

Брей Гетц (Bria Goetz), архитектор языка Oracle, опубликовал серию статей (еще черновиков) о текущем состоянии лямбда-выражений в Java. Он также охватывает закрытие, поскольку они планируют выпустить их в предстоящем JDK, который должен быть завершен полным кодом около января 2013 года и должен быть выпущен примерно в середине года 2013.
  • Состояние лямбда: на первой или второй странице эта статья пытается ответить на представленный здесь вопрос. Хотя я все еще не разбираюсь в аргументах, но он полон примеров.
  • Состояние Lambda-Libraries Edition: это тоже очень интересно, потому что оно охватывает такие преимущества, как ленивая оценка и parallelism.
  • Перевод выражений лямбда: это в основном объясняет процесс desugaring, выполняемый компилятором Java.

Ответ 13

Как разработчик java, который пытается научить себя lisp, чтобы стать лучшим программистом, я бы сказал, что хотел бы увидеть предложение Josh Block для закрытий. Я нахожу себя использующим анонимные внутренние классы, чтобы выражать такие вещи, как то, что делать с каждым элементом списка при агрегировании некоторых данных. Было бы неплохо представить это как замыкание, вместо того, чтобы создавать абстрактный класс.

Ответ 14

Закрытие на императивном языке (примеры: JavaScript, С#, предстоящее обновление С++) не совпадают с анонимными внутренними классами. Они должны уметь фиксировать модифицируемые ссылки на локальные переменные. Внутренние классы Java могут захватывать только локальные переменные final.

Почти любая функция языка может быть подвергнута критике как несущественная:

  • for, while, do - это всего лишь синтаксический сахар над goto/if.
  • Внутренние классы - это синтаксический сахар над классами с полем, указывающим на внешний класс.
  • Дженерики - это синтаксический сахар над листами.

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

Ответ 16

Не только этот benjismith, но мне нравится, как вы можете просто...

myArray.sort {it.myProperty}

Вам нужен только более подробный компаратор, который вы показали, когда сравнение свойств на естественном языке не соответствует вашим потребностям.

Мне очень нравится эта функция.

Ответ 17

Что касается читаемости и ремонтопригодности... закрытие однострочных линий сложнее понять и отладить, imo Программное обеспечение имеет долговременную жизнь, и вы можете получить людей с рудиментарными знаниями языка для его поддержания... Таким образом, разброс логики лучше, чем однострочный, для удобства обслуживания... Обычно у вас нет программной звезды, которая следит за программным обеспечением после релиз...

Ответ 18

Возможно, вам стоит взглянуть на Groovy, язык, который в основном совместим с Java, и работает на JRE, но поддерживает закрытие.

Ответ 19

Отсутствие привязки к анонимной функции [т. если переменные (и аргументы метода, если есть метод окружения) внешнего контекста объявлены final, тогда они доступны, но не иначе], я не совсем понимаю, что именно это ограничение фактически покупает.

Я использую "final" в любом случае. Итак, если бы я хотел использовать те же объекты внутри закрытия, я бы действительно объявил эти объекты окончательными в охватывающей области. Но что было бы неправильным в том, чтобы позволить "закрыть [java a.i.c.]" просто получить копию ссылки, как если бы она передавалась через конструктор (ну, на самом деле это так, как это делается).

Если закрытие хочет перезаписать ссылку, пусть будет так; он сделает это без изменения копии, видимой в охватывающей области.

Если мы будем утверждать, что это приведет к нечитаемому коду (например, возможно, не прямолинейно, чтобы увидеть, что ссылка на объект во время вызова конструктора для aic), то как же, по крайней мере, сделать синтаксис менее подробным? Scala? Groovy?