В Java Делегатов?

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

11 ответов


на самом деле нет.

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

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


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

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

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

рассказ: нет.

введение

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

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

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

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


вы читали этой :

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

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

obj.registerHandler (ano.methodOne);

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

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

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

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

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

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

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

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

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

в этой более общей ситуации подход 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 и в более общих ситуациях.

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


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


нет, но они fakeable через прокси и размышления:

  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 может помочь здесь), а затем безопасно использовать его для делегирования различным экземплярам.

посмотреть код карга на github дополнительные тесты и реализация.


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

Как Это Работает

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

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

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

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

ниже приведен пример использования обратный вызов для обработки файлов в дереве каталогов. Это начальный этап проверки, который просто подсчитывает файлы для обработки и гарантирует, что ни один из них не превышает заданного максимального размера. В этом случае мы просто создаем обратный вызов inline с вызовом 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 ():

/**
 * 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);</pre>
 * <p>
 * @return          The number of files processed.
 */
static public int processDirectory(File dir, Callback cbk, FileSelector sel) {
    return _processDirectory(dir,new 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++; }
        }
    else {
        cbk.invoke(dir,(Object[])null);

        File[] lst=(sel==null ? dir.listFiles() : dir.listFiles(sel));
        if(lst!=null) {
            for(int xa=0; xa<lst.length; xa++) {
                File ent=lst[xa];
                if(!ent.isDirectory()) {
                    cbk.invoke(dir,ent);
                    lst[xa]=null;
                    cnt++;
                    }
                }
            for(int xa=0; xa<lst.length; xa++) {
                File ent=lst[xa];
                if(ent!=null) { cnt+=_processDirectory(ent,cbk,sel); }
                }
            }
        }
    return cnt;
    }

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

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


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


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

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

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

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


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

Java Interface


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

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