В чем разница между анонимными классами в Java и замыканиями? - PullRequest
35 голосов
/ 20 июня 2011

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

Ответы [ 3 ]

14 голосов
/ 20 июня 2011

Разницы почти нет. На самом деле существует старая поговорка о замыканиях и объектах. Закрытия - это объект бедняка, а объекты - это закрытие бедняка. Оба одинаково сильны с точки зрения того, что они могут сделать. Мы спорим только из-за выразительности.

В Java мы моделируем замыкания с помощью анонимных объектов. На самом деле небольшая история здесь заключается в том, что изначально Java имела возможность изменять внешнюю область без использования final. Это работает и прекрасно работает для объектов, размещенных в локальной области метода, но когда дело доходит до примитивов, это вызывает много противоречий. Примитивы размещаются в стеке, поэтому для того, чтобы они жили после выполнения внешнего метода, Java должна была бы выделить память в куче и переместить эти элементы в кучу. В то время люди были совсем новичками в сборке мусора и не доверяли ей, так что утверждение было то, что Java не должна выделять память без явного указания программиста. В попытке достичь компромисса Java решила использовать ключевое слово final.

http://madbean.com/2003/mb2003-49/

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

public Object someFunction() {
   int someValue = 0;

   SomeAnonymousClass implementation = new SomeAnonymousClass() {
       public boolean callback() {
           someValue++;
       }
   }
   implementation.callback();
   return someValue;
}

Будет переписано в:

 public Object someFunction() {
   SomeAnonymousClass implementation = new SomeAnonymousClass() {
       public int someValue = 0;

       public boolean callback() {
           someValue++;
       }
   }
   implementation.callback();

   // all references to someValue could be rewritten to 
   // use this instance variable instead.
   return implementation.someValue;
}

Я думаю, что причина, по которой люди жалуются на анонимные внутренние классы, больше связана со статической типизацией, чем с динамической типизацией. В Java мы должны определить согласованный интерфейс для разработчика анонимного класса и кода, принимающего анонимный класс. Мы должны сделать это, чтобы мы могли проверять все во время компиляции. Если бы у нас были функции первого класса, тогда Java должен был бы определить синтаксис для объявления параметров метода и возвращаемых типов как типа данных, чтобы оставаться статически типизированным языком для безопасности типов. Это было бы почти так же сложно, как определить интерфейс. (Интерфейс может определять несколько методов, синтаксис для объявления методов 1-го класса будет только для одного метода). Вы можете думать об этом как о синтаксисе интерфейса короткой формы. Под капотом компилятор может преобразовать краткую запись формы в интерфейс во время компиляции.

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

9 голосов
/ 20 июня 2011

Поскольку они оба влияют на "приватную" область видимости, в очень ограниченном смысле, да. однако, существует так много различий, что ответом может быть «нет».

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

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

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

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

4 голосов
/ 20 июня 2011

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

Допустим, вы хотите использовать локальную переменную с использованием анонимного класса.

final int[] i = { 0 };
final double[] d = { 0.0 };

Runnable run = new Runnable() { 
    public void run() {
        d[0] = i[0] * 1.5;
    }
};
executor.submit(run);

Затворы исключают необходимость в большинстве кодировок котельной пластины, позволяя писать только то, что предназначено.

int i = 0;
double d = 0.0;

Runnable run = { => d = i * 1.5; };
executor.submit(run);

или даже

executor.submit({ => d = i * 1.5; });

или если затворы поддерживают кодовые блоки.

executor.submit() {
    d = i * 1.5; 
}
...