Есть ли делегаты в Java 8?

Есть ли делегаты в Java 8?

Если нет, то как у нас есть лямбда-выражения в JDK 8 без делегатов?

Что такое ссылки методов? Являются ли они такими же, как делегаты?

Ответы

Ответ 1

В JDK 8 нет делегатов. Под капотом lambdas являются экземплярами функциональных интерфейсов (интерфейс с одним абстрактным методом). В зависимости от того, где вы передаете свой лямбда, компилятор может определить, какой интерфейс он реализует. Например, метод Collections.sort принимает экземпляр Comparator как второй параметр. Компаратор является функциональным интерфейсом, поэтому компилятор проверяет, соответствует ли проходящая лямбда абстрактному методу в компараторе или нет.

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

вместо:

Arrays.sort(rosterAsArray,
    (a, b) -> Person.compareByAge(a, b)
);

проще с ссылкой на метод:

Arrays.sort(rosterAsArray, Person::compareByAge);

Посмотрите lambdafaq.

Ответ 2

Спасибо Pacerier за комментарий к этому вопросу, вот способ выполнить то, что делает делегат С# (однофункционал), даже в Java 7 или ниже.

// This defines the 'delegate'.
public interface IA {
    int f(int a);
}

public class MyClass {
    // f1 and f2 have the same signature as 'IA.f'.
    private int f1(int a) {
        return a + 1;
    }

    private int f2(int a) {
        return 2 * a;
    }

    // These wrappers are one way to return a 'delegate'.
    // Each wrapper creates one instance of an anonymous class.
    // Notice that we did not have to declare MyClass as implementing IA,
    // and that we can wrap different methods of MyClass into 'IA's.
    // Contrast this with 'MyClass implements IA', which would require
    // a method 'f' in 'MyClass', and would not provide a way to
    // delegate to different methods of 'MyClass'.
    public IA wrapF1() {
        return (new IA(){
            public int f(int a) {
                return f1(a);
            }
        });
    }

    public IA wrapF2() {
        return (new IA(){
            public int f(int a) {
                return f2(a);
            }
        });
    }

    // returns a 'delegate', either to 'f1' or 'f2'.
    public IA callMe(boolean useF2) {
        if (!useF2)
            return wrapF1();
        else
            return wrapF2();
    }

}

Использование

...
// Create and use three 'delegates'.
// Technically, these are not quite the same as C# delegates,
// because we have to invoke a method on each.
// That is, we must do 'ia0.f(..)' instead of 'ia0(..)'.
// Nevertheless, it satisfies the design requirement.
MyClass c = new MyClass();
IA ia0 = c.wrapF1();
IA ia1 = c.callMe(false);
IA ia2 = c.callMe(true);
int result0 = ia0.f(13);
int result1 = ia1.f(13);
int result2 = ia2.f(13);

дает

result0: 14    <-- f1, so 13 + 1
result1: 14    <-- f1, so 13 + 1
result2: 26    <-- f2, so 2 * 13

ПРИМЕЧАНИЕ. Если вам нужна только одна реализация для каждого класса данного "делегата", то более простым решением является непосредственное внедрение интерфейса в класс. Вот пример. Класс уже имел f3, и теперь он расширен для реализации IA:

public class MyClass2
        implements IA {
    private int baseValue;
    private int anotherValue;

    public MyClass2(int baseValue, int anotherValue) {
        this.baseValue = baseValue;
        this.anotherValue = anotherValue;
    }

    public int f3(int v1, int v2) {
        return 2 * v1 + v2;
    }

    public int f(int a) {
        return f3(baseValue + a, anotherValue);
    }
}

IA ia3 = new MyClass2(10, 3);
int result3 = ia3.f(13);   // = f3(10 + 13) = 2 * 23 + 3 = 49

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

Верно, что результат не совсем такой же, как делегат С#, потому что нужно сделать ia.f(), а не ia(). Тем не менее, цель проекта была достигнута.


ПРИМЕЧАНИЕ. В Java 8 кодирование упрощается с помощью lambdas. Я не использую Java 8, поэтому я не буду включать реализацию Java 8 здесь. (Любой желающий может отправить редактирование, которое добавляет эту реализацию.Я предлагаю показать новые тела ниже для моих wrapF1() и wrapF2() выше, поскольку это упростит сравнение версий Java 7 и Java 8.)