Вопрос

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

Это было полезно?

Решение

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

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

Возможно, вам также будет интересна/полезна эта статья: Программист 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 представленные для его поддержки, не являются частью JavaТМязык программирования, который определяется Язычная спецификация и изменено Спецификация внутренних классов включено в документация для программного обеспечения JDKTM 1.1.

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

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

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

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

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

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

obj.registerHandler(ano.methodOne);

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

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

Внутренние классы 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 и в более общих ситуациях.

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

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

Нет, но их можно подделать с помощью прокси и отражений:

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

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

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

Таким образом, вложенный класс WithParms является необязательным и служит двум целям: он содержит массив объектов параметров, необходимый для вызовов обратного вызова, и предоставляет 10 перегруженных методов вызова() (с от 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():

/**
 * 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 требуется класс, расширяющий Thread или реализующий Runnable, поскольку переменная объекта класса может использоваться как указатель местоположения в памяти.

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

Java Interface

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

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

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top