Представители Java?

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

Ответы

Ответ 1

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

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

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

Ответ 2

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

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

// 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 в С#, используйте вместо этого ссылку 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 */ });

Ответ 3

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

Введение

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

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

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

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

Ответ 4

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

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

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

obj.registerHandler(ano.methodOne);

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

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

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

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

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

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

Ответ 5

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

Ответ 6

Нет, но они поддельные, используя прокси и рефлексию:

  public static class TestClass {
      public String knockKnock() {
          return "who 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 there?"));
  }

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

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

Ответ 7

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

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

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

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

Для того, чтобы быть потокобезопасным, массив параметров должен существовать уникально для каждого вызова метода API, а для эффективности тот же самый должен использоваться для каждого вызова обратного вызова; Мне понадобился второй объект, который был бы дешевым для создания, чтобы связать обратный вызов с массивом параметров для вызова. Но в некоторых сценариях у invoker уже был массив параметров по другим причинам. По этим двум причинам массив параметров не принадлежит объекту обратного вызова. Также выбор вызова (передача параметров как массива или отдельных объектов) принадлежит к 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;
    }

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

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

Ответ 8

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

Ответ 9

Нет, но он имеет подобное поведение, внутренне.

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

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

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

Ответ 10

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

Java Interface

Ответ 11

Он не имеет явного ключевого слова delegate как С#, но вы можете достичь аналогичного в Java 8, используя функциональный интерфейс (т.е. Любой интерфейс с ровно одним методом) и лямбду:

private interface SingleFunc {
    void printMe();
}

public static void main(String[] args) {
    SingleFunc sf = () -> {
        System.out.println("Hello, I am a simple single func.");
    };
    SingleFunc sfComplex = () -> {
        System.out.println("Hello, I am a COMPLEX single func.");
    };
    delegate(sf);
    delegate(sfComplex);
}

private static void delegate(SingleFunc f) {
    f.printMe();
}

Каждый новый объект типа SingleFunc должен реализовывать printMe(), поэтому можно безопасно передать его другому методу (например, delegate(SingleFunc)) для вызова printMe().

Ответ 12

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

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