Закрытие и обратные вызовы - PullRequest
8 голосов
/ 18 марта 2010

Есть ли другой способ в java для реализации обратных вызовов, кроме внутренних классов? В чем разница между обратными вызовами и замыканиями?

Ответы [ 9 ]

10 голосов
/ 18 марта 2010

Закрытие - это то, как вы его строите, обратный вызов - это то, как вы его используете.

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

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

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

5 голосов
/ 18 марта 2010

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

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

var f, g;
function foo() {
  var x = 0;
  f = function() { return ++x; };
  g = function() { return --x; };
  x = 1;
  alert('inside foo, call to f(): ' + f()); // "2"
}
foo();
alert('call to g(): ' + g()); // "1"
alert('call to f(): ' + f()); // "2"
1 голос
/ 18 марта 2010

Если вам нужны замыкания в Java, вы можете попробовать lambdaj . Здесь вы можете увидеть, как это позволяет определять замыкания через очень простой DSL.

1 голос
/ 18 марта 2010

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

Слово «закрытие» несколько злоупотребляет, и многие люди просто используют его как синоним «анонимной функции», но, по крайней мере, , согласно Википедии , это неправильное использование этого термина. Статья в Википедии объясняет это лучше, чем я могу сделать быстро.

0 голосов
/ 05 апреля 2013

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

А вот лучший пример (можно найти здесь http://www.caglargonul.com/2013/04/10/is-it-really-a-closure-yes-it-is/) для понимания того, что такое замыкание. Ключ в том, что

замыкание происходит со ссылочной средой, а не просто с кодом функции.

Лучший способ реализовать замыкание в Java 7 и ниже - использовать интерфейс. В этом примере обратный вызов реализован как замыкание.

Сначала вы объявляете свой интерфейс, который будет удерживать ваше закрытие.

public interface CallBack {
    void m(int e);
}

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

public class CCallBack {

    List<CallBack> cbs = new ArrayList<>();

    public void registerCallBack(CallBack f){
        cbs.add(f);
    }

    public void removeCallBack(CallBack f){
        if(cbs.contains(f)){
            cbs.remove(f);
        }
    }

    public void onAction(int i){
        for (CallBack callBack : cbs) {
            callBack.m(i);
        }
    }
}

А вот и волшебная часть. См. Ссылочную среду в действии.

public class CallBackTester {
    CCallBack cb = new CCallBack();

    @Test
    public void test_callback(){

        CallBack cb1 = new CallBack() {
            int x = 1;
            @Override
            public void m(int e) {
                if(e==1){
                    System.out.println("You register this callback " + x + " time/times");
                    x++;
                }
            }
        };

        cb.registerCallBack(cb1);
        cb.registerCallBack(cb1);
        cb.registerCallBack(cb1);
        cb.removeCallBack(cb1);

        cb.onAction(1);
    }

}

Выше, когда мы объявляем cb1, мы добавляем среду ссылки, которая состоит из переменной x. Когда мы вызываем функцию внутри этого замыкания, мы увеличиваем эту переменную на единицу. Если бы это была нормальная функция, x был бы объявлен как 1, когда мы вызываем функцию. НО ЭТО НЕ НОРМАЛЬНАЯ ФУНКЦИЯ. ЭТО ЗАКРЫТИЕ. Поэтому x не объявляется каждый раз, когда мы вызываем функцию в замыкании. Как видно из выходных данных каждый раз, когда мы его вызываем, x увеличивается.

You register this callback 1 time/times
You register this callback 2 time/times
0 голосов
/ 18 марта 2010

На данный момент анонимные классы являются лучшим способом обработки обратных вызовов в Java. Однако это может измениться в Java 7, которая будет реализовывать замыкания. http://en.wikipedia.org/wiki/Closure_(computer_science)

0 голосов
/ 18 марта 2010

К сожалению, единственный разумный способ - это внутренние / анонимные классы.

Вы также можете сделать это с отражением, но это обычно медленнее и сложнее в обслуживании (нет подсветки синтаксиса, трудно найти ссылки в IDE и т. Д.). Пример:

myButton.addActionListener(EventHandler.create(ActionListener.class, handlerObject, "onClick"));
0 голосов
/ 18 марта 2010

Разницы нет.

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

На самом деле, единственное различие, которое я знаю между ними, - это простота написания. Типичное закрытие groovy / ruby ​​действительно меньше для записи, чем анонимный класс Java.

Однако, учитывая Java-фреймворки, такие как guava и либеральное использование анонимных классов / интерфейсов, особенно для типичных замыканий, такие как filter (в сравнении с реализацией groovy ) Я могу сказать, что нет абсолютно никакой разницы в дизайне.

0 голосов
/ 18 марта 2010

Я так не думаю.

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

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...