Qu'est-ce qu'un «type SAM» en Java?

133

En lisant sur la spécification Java-8, je continue de voir des références aux «types SAM». Je n'ai pas été en mesure de trouver une explication claire de ce que c'est.

Qu'est-ce qu'un type SAM et quel est un exemple de scénario dans lequel on pourrait en utiliser un?

Cody
la source
4
Voir cr.openjdk.java.net/~briangoetz/lambda/lambda-state-3.html (que j'ai trouvé après une seule recherche, qui m'a amené à une autre question SO).
Jon Skeet
2
Veuillez ne pas renvoyer les gens vers des informations obsolètes. Une recherche un peu plus longue vous aurait conduit à la version la plus à jour: cr.openjdk.java.net/~briangoetz/lambda/lambda-state-4.html , qui elle-même a 18 mois et est maintenant obsolète dans de nombreux des endroits. La question de l'OP est répondue à lambdafaq.org/what-is-a-functional-interface , une page dans une FAQ que j'essaie de tenir à jour dans ce qui a été jusqu'à récemment un langage en évolution rapide et le développement d'API.
Maurice Naftalin
1
@MauriceNaftalin Vous pouvez également simplement relier le sentier Java , qui est tenu à jour par l'équipe de développement.
Brian
1
Le terme actuel est «interface fonctionnelle».
newacct
1
@MauriceNaftalin: Désolé, j'ai raté celui-là. J'aurais certainement lié à cela si j'avais trouvé cela.
Jon Skeet

Réponses:

142

Pour résumer le lien Jon publié 1 dans le cas où il ne va jamais vers le bas, « SAM » signifie « méthode abstraite unique » et « SAM type » fait référence à des interfaces comme Runnable, Callable, etc. expressions Lambda, une nouvelle fonctionnalité en Java 8, sont considéré comme un type SAM et peut être librement converti en eux.

Par exemple, avec une interface comme celle-ci:

public interface Callable<T> {
    public T call();
}

Vous pouvez déclarer une Callableutilisation d'expressions lambda comme ceci:

Callable<String> strCallable = () -> "Hello world!";
System.out.println(strCallable.call()); // prints "Hello world!"

Les expressions lambda dans ce contexte ne sont pour la plupart que du sucre syntaxique. Ils ont une meilleure apparence dans le code que les classes anonymes et sont moins restrictifs sur la dénomination des méthodes. Prenez cet exemple sur le lien:

class Person { 
    private final String name;
    private final int age;

    public static int compareByAge(Person a, Person b) { ... }

    public static int compareByName(Person a, Person b) { ... }
}

Person[] people = ...
Arrays.sort(people, Person::compareByAge);

Cela crée une Comparatorutilisation d'une méthode spécifique qui ne partage pas le même nom que Comparator.compare, de cette façon, vous n'avez pas à suivre la dénomination de l'interface des méthodes et vous pouvez avoir plusieurs remplacements de comparaison dans une classe, puis créer les comparateurs à la volée via les expressions lambda.

Aller plus loin ...

À un niveau plus profond, Java les implémente en utilisant l' invokedynamicinstruction bytecode ajoutée dans Java 7. J'ai dit plus tôt que déclarer un Lambda crée une instance Callableou Comparablesimilaire à une classe anonyme, mais ce n'est pas strictement vrai. Au lieu de cela, la première fois que le invokedynamicest appelé, il crée un gestionnaire de fonction Lambda à l'aide de la LambdaMetafactory.metafactoryméthode , puis utilise cette instance mise en cache dans les futurs appels de Lambda. Plus d'informations peuvent être trouvées dans cette réponse .

Cette approche est complexe et inclut même du code qui peut lire des valeurs primitives et des références directement à partir de la mémoire de la pile pour passer dans votre code Lambda (par exemple pour contourner le besoin d'allouer un Object[]tableau pour appeler votre Lambda), mais elle permet de futures itérations de l'implémentation Lambda pour remplacer les anciennes implémentations sans avoir à se soucier de la compatibilité du bytecode. Si les ingénieurs d'Oracle modifient l'implémentation Lambda sous-jacente dans une version plus récente de la JVM, Lambdas compilé sur une ancienne JVM utilisera automatiquement l'implémentation la plus récente sans aucune modification de la part du développeur.


1 La syntaxe du lien est obsolète. Jetez un œil à la piste Java des expressions Lambda pour voir la syntaxe actuelle.

Brian
la source