Делегаты Java?

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

189
задан Mark 4 September 2008 в 11:45
поделиться

1 ответ

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

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

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

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

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

Вложенные классы с Apparms, затем являются необязательными и служат двумя целями, он содержит массив объекта параметра, необходимый для вызовов обратного вызова, и он предоставляет 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;
    }

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

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

2
ответ дан 23 November 2019 в 05:40
поделиться
Другие вопросы по тегам:

Похожие вопросы: