it-roy-ru.com

Зачем мне нужен функциональный интерфейс для работы с лямбдами?

Я думаю, что этот вопрос уже где-то там, но я не смог его найти.

Я не понимаю, зачем нужен функциональный интерфейс для работы с лямбдами. Рассмотрим следующий пример:

public class Test {

    public static void main(String...args) {
        TestInterface i = () -> System.out.println("Hans");
//      i = (String a) -> System.out.println(a);

        i.hans();
//      i.hans("Hello");
    }
}

public interface TestInterface {
    public void hans();
//  public void hans(String a);
}

Это работает без проблем, но если вы раскомментируете закомментированные строки, это не так. Зачем? В моем понимании компилятор должен уметь различать два метода, поскольку они имеют разные входные параметры. Зачем мне нужен функциональный интерфейс и взорвать мой код?

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

Правка 2: Извините, я, очевидно, не носитель языка, но чтобы уточнить себя:

public interface TestInterface {
    public void hans();                 //has no input parameters</br>
    public void hans(String a);         //has 1 input parameter, type String</br>
    public void hans(String a, int b);  //has 2 input parameters, 1. type = String, 2. type = int</br>
    public void hans(int a, int b);     //has also 2 input parameters, but not the same and a different order than `hans(String a, int a);`, so you could distinguish both
}

public class Test {

    public static void main(String...args) {
        TestInterface i = () -> System.out.println("Hans");
        i = (String a) -> System.out.println(a);
        i = (String a, int b) -> System.out.println(a + b);
        i = (int a, int b) -> System.out.println(a);

        i.hans(2, 3);   //Which method would be called? Of course the one that would take 2 integer arguments. :)
    }
}

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

29
codepleb

Когда вы пишете:

TestInterface i = () -> System.out.println("Hans");

Вы даете реализацию методу void hans()TestInterface.

Если бы вы могли назначить лямбда-выражение интерфейсу, имеющему более одного абстрактного метода (то есть нефункциональный интерфейс), лямбда-выражение могло бы реализовать только один из методов, оставив другие методы нереализованными.

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

28
Eran

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

interface TestInterface {
    void first();
    void second(); // this is only distinguished from first() by method name
    String third(); // maybe you could say in this instance "well the return type is different"
    Object fourth(); // but a String is an Object, too !
}

void test() {
    // which method are you implementing, first or second ?
    TestInterface a = () -> System.out.println("Ido mein ado mein");
    // which method are you implementing, third or fourth ?
    TestInterface b = () -> "Ido mein ado mein";
}
17
blagae

Вы, кажется, ищете анонимные классы . Следующий код работает:

public class Test {

    public static void main(String...args) {
        TestInterface i = new TestInterface() {
            public void hans() {
                System.out.println("Hans");
            }
            public void hans(String a) {
                System.out.println(a);
            }
        };

        i.hans();
        i.hans("Hello");
    }
}

public interface TestInterface {
    public void hans();
    public void hans(String a);
}

Лямбда-выражения (в основном) являются более коротким способом написания анонимных классов только одним методом. (Аналогично, анонимные классы являются сокращением для внутренних классов, которые вы используете только в одном месте)

5
immibis

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

В вашем случае вы можете использовать уже существующий интерфейс Runable 

Runnable r = () -> System.out.println("Hans");

а затем позвоните 

r.run();

Вы можете думать о лямбде -> как о короткой руке для: 

Runnable r = new Runnable() {
     void run() {
          System.out.println("Hans");`
     }
}

С лямбдой вам не нужен анонимный класс, который создается под капотом в примере выше. 

Но это имеет некоторое ограничение, чтобы выяснить, какой метод должен называться интерфейс, используемый с лямбдами, должен быть SAM (Single Abstract Method). Тогда у нас есть только один метод. 

Для более подробного объяснения читайте: 

Введение в функциональные интерфейсы - Концепция воссоздана в Java 8

3
Damian Leszczyński - Vash

Функциональный интерфейс может содержать только один абстрактный метод в соответствии с спецификациями Java

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

Например, Runnable - это встроенный функциональный интерфейс:

public interface Runnable() {
    public void run();
}

Использование может быть продемонстрировано, как показано ниже:

public class LambdaTest {
    // data type of parameter 'task' is functional interface 'Runnable'
    static void doSeveralTimes(Runnable task, int repeat) {
        for (int i = 0; i < repeat; i++) {
            task.run();
        }
    }

    public static void main(String[] args) {
        // one-time lambda
        doSeveralTimes(() -> {
            System.out.println("one-time lambda");
        }, 3);

        // lambda as variable
        Runnable test;
        test = () -> {
            System.out.println("lambda as variable");
        };
        doSeveralTimes(test, 3);
    }
}

и результат:

one-time lambda
one-time lambda
one-time lambda
lambda as variable
lambda as variable
lambda as variable
0
mzoz