Делегаты Java? - PullRequest
       28

Делегаты Java?

184 голосов
/ 05 сентября 2008

Имеет ли язык Java функции делегатов, аналогично тому, как в C # есть поддержка делегатов?

Ответы [ 11 ]

145 голосов
/ 05 сентября 2008

Не совсем, нет.

Вы можете достичь того же эффекта, используя отражение, чтобы получить объекты Method, которые затем можете вызывать, а другой способ - создать интерфейс с единственным методом invoke или execute, а затем создать их вызовите интересующий вас метод (т. е. используйте анонимный внутренний класс).

Вы также можете найти эту статью интересной / полезной: Java-программист смотрит на делегатов C #

56 голосов
/ 13 апреля 2012

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

Вместо такой строки, которая объявляет подпись именованного метода:

// C#
public delegate void SomeFunction();

объявить интерфейс:

// Java
public interface ISomeBehaviour {
   void SomeFunction();
}

Для конкретных реализаций метода определите класс, который реализует поведение:

// Java
public class TypeABehaviour implements ISomeBehaviour {
   public void SomeFunction() {
      // TypeA behaviour
   }
}

public class TypeBBehaviour implements ISomeBehaviour {
   public void SomeFunction() {
      // TypeB behaviour
   }
}

Тогда, где бы у вас ни был делегат SomeFunction в C #, используйте вместо него ссылку ISomeBehaviour:

// C#
SomeFunction doSomething = SomeMethod;
doSomething();
doSomething = SomeOtherMethod;
doSomething();

// Java
ISomeBehaviour someBehaviour = new TypeABehaviour();
someBehaviour.SomeFunction();
someBehaviour = new TypeBBehaviour();
someBehaviour.SomeFunction();

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

// Java
public void SomeMethod(ISomeBehaviour pSomeBehaviour) {
   ...
}

...

SomeMethod(new ISomeBehaviour() { 
   @Override
   public void SomeFunction() {
      // your implementation
   }
});

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

И, конечно же, в Java 8 они становятся в основном лямбда-выражениями:

// Java 8
SomeMethod(() -> { /* your implementation */ });
35 голосов
/ 05 сентября 2008

Короткая история: нет .

Введение

Новейшая версия среды разработки Microsoft Visual J ++ поддерживает языковую конструкцию с именем делегатов или связанный метод ссылки . Эта конструкция и новые ключевые слова delegate и multicast, представленный для его поддержки, не является частью Java TM язык программирования, который определяется Java Language Спецификация с изменениями Спецификация внутренних классов включена в документации для программного обеспечения JDKTM 1.1 .

Маловероятно, что язык программирования Java будет когда-либо включать эта конструкция. Sun уже тщательно продумала принятие его в 1996 году, в меру построения и выбрасывания рабочих прототипов. наш Был сделан вывод, что ссылки на связанные методы не нужны и вредно для языка. Это решение было принято в консультации с Borland International, который имел предыдущий опыт работы с ссылки на методы в Delphi Object Pascal.

Мы считаем, что связанные ссылки на метод не нужны , потому что другой альтернатива конструкции, внутренние классы , обеспечивает равный или превосходящий функциональность. В частности, внутренние классы полностью поддерживают требования обработки событий пользовательского интерфейса, и были использованы для реализовать API пользовательского интерфейса, по крайней мере, такой же всеобъемлющий, как Классы Windows Foundation.

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

18 голосов
/ 17 октября 2010

Вы читали это :

Делегаты - полезная конструкция в системах, основанных на событиях. по существу Делегаты - это объекты, которые кодируют отправку метода для указанного объект. Этот документ показывает, как внутренние классы Java обеспечивают более общее решение таких проблем.

Что такое делегат? На самом деле это очень похоже на указатель на член функция, используемая в C ++. Но делегат содержит целевой объект вместе с методом, который будет вызван. В идеале было бы неплохо быть могу сказать:

obj.registerHandler (ano.methodOne);

.. и что метод methodOne будет вызван ano, когда будет получено какое-то конкретное событие.

Это то, чего достигает структура Делегата.

Внутренние классы Java

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

obj.registerHandler(new Handler() {
        public void handleIt(Event ev) {
            methodOne(ev);
        }
      } );

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

Общий обработчик

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

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

В этой более общей ситуации подход Java может обеспечить очень элегантное решение, особенно в сочетании с использованием окончательного переменные:

void processState(final T1 p1, final T2 dispatch) { 
  final int a1 = someCalculation();

  m_obj.registerHandler(new Handler() {
    public void handleIt(Event ev) {
     dispatch.methodOne(a1, ev, p1);
    }
  } );
}

финал * финал * финал

Вас привлекло внимание?

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

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

13 голосов
/ 15 февраля 2014

Я знаю, что этот пост старый, но в Java 8 добавлены лямбда-выражения и концепция функционального интерфейса, который представляет собой любой интерфейс только с одним методом. Вместе они предлагают аналогичную функциональность делегатам C #. Смотрите здесь для получения дополнительной информации, или просто Google Java Lambdas. http://cr.openjdk.java.net/~briangoetz/lambda/lambda-state-final.html

5 голосов
/ 10 февраля 2012

Нет, но их можно подделать, используя прокси и отражения:

  public static class TestClass {
      public String knockKnock() {
          return "who's there?";
      }
  }

  private final TestClass testInstance = new TestClass();

  @Test public void
  can_delegate_a_single_method_interface_to_an_instance() throws Exception {
      Delegator<TestClass, Callable<String>> knockKnockDelegator = Delegator.ofMethod("knockKnock")
                                                                   .of(TestClass.class)
                                                                   .to(Callable.class);
      Callable<String> callable = knockKnockDelegator.delegateTo(testInstance);
      assertThat(callable.call(), is("who's there?"));
  }

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

См. Код karg на github для дополнительных тестов и реализации .

2 голосов
/ 16 января 2010

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

Как это работает

Существует основной класс с именем Callback с вложенным классом с именем WithParms. API, которому необходим обратный вызов, примет объект Callback в качестве параметра и, если необходимо, создаст Callback.WithParms в качестве переменной метода. Поскольку многие приложения этого объекта будут рекурсивными, это работает очень чисто.

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

Чтобы быть потокобезопасным, массив параметров должен существовать уникально для каждого вызова метода API, и для эффективности тот же самый должен использоваться для каждого вызова обратного вызова; Мне нужен был второй объект, который было бы дешево создать, чтобы связать обратный вызов с массивом параметров для вызова. Но в некоторых случаях вызывающий уже имел массив параметров по другим причинам. По этим двум причинам массив параметров не принадлежит объекту Callback. Кроме того, выбор вызова (передача параметров в виде массива или отдельных объектов) принадлежит API, использующему обратный вызов, позволяющий ему использовать любой вызов, наиболее подходящий для его внутренней работы.

Тогда вложенный класс WithParms является необязательным и служит двум целям: он содержит массив объектов параметров, необходимый для вызовов обратного вызова, и предоставляет 10 перегруженных методов invoke () (с 1 до 10 параметрами), которые загружают параметр массив, а затем вызвать цель обратного вызова.

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

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

IoUtil.processDirectory ():

<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

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

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

0 голосов
/ 06 мая 2014

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

Java Interface

0 голосов
/ 06 декабря 2012

Java не имеет делегатов и гордится этим :). Из того, что я здесь прочитал, я нашел, по сути, 2 способа фальсификации делегатов: 1. отражение; 2. внутренний класс

Размышления неопрятные! Внутренний класс не охватывает самый простой вариант использования: функция сортировки. Не хочу вдаваться в подробности, но решение с внутренним классом в основном заключается в создании класса-оболочки для массива целых чисел, которые будут отсортированы в порядке возрастания, и класса для массива целых чисел, которые будут отсортированы в порядке убывания.

0 голосов
/ 26 марта 2012

Нет, но внутреннее поведение аналогичное.

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

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

Например, для создания потоков в Java требуется расширение класса Thread или реализация Runnable, поскольку переменная объекта класса может использоваться в качестве указателя местоположения в памяти.

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