web-dev-qa-db-fra.com

Pourquoi ai-je besoin d'une interface fonctionnelle pour travailler avec lambdas?

Je pense que cette question existe déjà quelque part, mais je n'ai pas réussi à la trouver.

Je ne comprends pas pourquoi il est nécessaire d'avoir une interface fonctionnelle pour travailler avec lambdas. Prenons l'exemple suivant:

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);
}

Cela fonctionne sans problème, mais si vous décommentez les lignes commentées, ce n'est pas le cas. Pourquoi? À ma connaissance, le compilateur devrait pouvoir distinguer les deux méthodes, car elles ont des paramètres d'entrée différents. Pourquoi ai-je besoin d'une interface fonctionnelle et de l'explosion de mon code?

EDIT: Les doublons liés ne répondent pas à ma question car je pose des questions sur différents paramètres de méthode. Mais j’ai eu des réponses très utiles ici, merci à tous ceux qui ont aidé! :)

EDIT2: Désolé, je ne suis évidemment pas un locuteur natif, mais pour me préciser:

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. :)
    }
}

Tout ce que je demande concerne les arguments. Le nom de la méthode importe peu, mais chaque méthode prend un ordre unique d'arguments différents et à cause de cela, Oracle aurait pu implémenter cette fonctionnalité à la place en rendant une seule méthode possible par "Interface Lambda".

29
codepleb

Quand tu écris:

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

Vous donnez une implémentation à la méthode void hans() de TestInterface.

Si vous pouviez affecter une expression lambda à une interface ayant plus d'une méthode abstraite (c'est-à-dire une interface non fonctionnelle), l'expression lambda ne pourrait implémenter qu'une des méthodes, en laissant les autres méthodes non implémentées.

Vous ne pouvez pas le résoudre en assignant deux expressions lambda ayant des signatures différentes à la même variable (comme vous ne pouvez pas affecter de références à deux objets à une seule variable et vous attendre à ce que cette variable fasse référence aux deux objets en même temps).

28
Eran

La raison la plus importante pour laquelle ils ne doivent contenir qu'une méthode est que la confusion est facilement possible autrement. Si plusieurs méthodes étaient autorisées dans l'interface, quelle méthode un lambda devrait-il choisir si les listes d'arguments sont les mêmes?

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

Vous semblez être à la recherche de classes anonymes . Le code suivant fonctionne:

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);
}

Les expressions Lambda sont (généralement) un moyen plus court d’écrire des classes anonymes avec une seule méthode. (De même, les classes anonymes sont un raccourci pour les classes internes que vous utilisez uniquement à un seul endroit)

5
immibis

L'interface fonctionnelle ne peut contenir qu'une seule méthode abstraite selon specs Java

Certes, l'expression lambda peut être utilisée une seule fois comme votre code commenté, mais lorsqu'il s'agit de passer l'expression lambda en tant que paramètre pour imiter le rappel de fonction, l'interface fonctionnelle est indispensable car dans ce cas, le type de données variable est l'interface fonctionnelle. 

Par exemple, Runnable est une interface fonctionnelle intégrée:

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

L'utilisation peut être démontrée comme ci-dessous:

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);
    }
}

et le résultat est:

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