Программирование на языке: Каков самый элегантный способ определения обратных вызовов в Java? - PullRequest
4 голосов
/ 02 мая 2009

В книге 'Code Complete' автор говорит о программировании на языке (вместо программирования на языке). Он означает, что вы не должны ограничивать себя ограничениями выбранного языка программирования.

Обратный вызов является часто используемой функцией. Меня интересует: Каков самый элегантный способ программирования обратных вызовов на язык Java?

Ответы [ 6 ]

6 голосов
/ 02 мая 2009

Java использует все виды обратных вызовов для всех видов ситуаций. Начиная с самых старых старых дней слушателей AWT, Java была все о обратных вызовах.

Существует два основных «варианта» обратных вызовов Java. Первый - это реализовать метод интерфейса:

public class MyThing implements StateChangeListener {

   //this method is declared in StateChangeListener
   public void stateChanged() {
      System.out.println("Callback called!");
   }

   public MyThing() {
      //Here we declare ourselves as a listener, which will eventually
      //lead to the stateChanged method being called.
      SomeLibraryICareAbout.addListener(this);
   }
}

Вторым вариантом обратного вызова Java является анонимный внутренний класс:

public class MyThing {

   public MyThing() {
      //Here we declare ourselves as a listener, which will eventually
      //lead to the stateChanged method being called.
      SomeLibraryICareAbout.addListener( new StateChangeListener() {
          //this method is declared in StateChangeListener
          public void stateChanged() {
              System.out.println("Callback called!");
          }
      });
   }
}

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

4 голосов
/ 02 мая 2009

Самый распространенный способ обойти отсутствие указателей / делегатов функций в Java - это использование функторов.

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

public interface Callback<T,V>{
  public T invoke(V context);
}

Это намного более многословно, чем эквиваленты C / C ++ или C #, но это работает. Примером этого шаблона в стандартной библиотеке является интерфейс Comparator.

2 голосов
/ 02 мая 2009

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

public interface MyCallback
{
    public void theCallback(int arg);
}

public class Sample
{
    public static void takesACallback(MyCallback callback)
    {
        ...
        callback.theCallback(arg);
    }
}

public class Sample2
{
    public static void main(String[] args)
    {
        Sample.takesACallback(new MyCallback()
        {
            void theCallback(int arg)
            {
                // do a little dance
            }
        });
    }
}
1 голос
/ 02 мая 2009

Ознакомьтесь с этой статьей:

http://www.onjava.com/pub/a/onjava/2003/05/21/delegates.html

Обратные вызовы, по сути, являются особым случаем делегатов (поскольку в C # они есть), и в статье дается реализация чего-то, напоминающего делегат C #, в java.

0 голосов
/ 02 мая 2009

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

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

Например, обход дерева файловой системы для обработки каждого файла:

API дерева каталогов процессов

<code>/**
 * Process a directory using callbacks.  To interrupt, the callback must throw an (unchecked) exception.
 * Subdirectories are processed only if the selector is null or selects the directories, and are done
 * after the files in any given directory.  When the callback is invoked for a directory, the file
 * argument is null;
 * <p>
 * The callback signature is:
 * <pre>    void callback(File dir, File ent);
*

* @return Количество обработанных файлов. * / static public int processDirectory (File dir, Callback cbk, FileSelector sel) { return _processDirectory (dir, новый Callback.WithParms (cbk, 2), sel); } static private int _processDirectory (File dir, Callback.WithParms cbk, FileSelector sel) { int cnt = 0; if (! dir.isDirectory ()) { if (sel == null || sel.accept (dir)) {cbk.invoke (dir.getParent (), dir); CNT ++; } } еще { cbk.invoke (реж, (Object []) NULL); File [] lst = (sel == null? Dir.listFiles (): dir.listFiles (sel)); if (lst! = null) { для (int xa = 0; xa

Использование API-интерфейса Process Directory

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

static private final Method             COUNT =Callback.getMethod(Xxx.class,"callback_count",true,File.class,File.class);

...

IoUtil.processDirectory(root,new Callback(this,COUNT),selector);

...

private void callback_count(File dir, File fil) {
    if(fil!=null) {                                                             // file is null for processing a directory
        fileTotal++;
        if(fil.length()>fileSizeLimit) {
            throw new Abort("Failed","File size exceeds maximum of "+TextUtil.formatNumber(fileSizeLimit)+" bytes: "+fil);
            }
        }
    progress("Counting",dir,fileTotal);
    }
0 голосов
/ 02 мая 2009

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

Взгляните на http://java.sun.com/docs/books/tutorial/uiswing/events/actionlistener.html

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

listener = new ActionListener() {
  public void actionPerformed(ActionEvent e) {
     // do stuff...
  }
};

где вы затем передаете слушателю соответствующий метод Swing.

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