Supposons que j'ai une interface générique:
interface MyComparable<T extends Comparable<T>> {
public int compare(T obj1, T obj2);
}
Et une méthode sort
:
public static <T extends Comparable<T>>
void sort(List<T> list, MyComparable<T> comp) {
// sort the list
}
Je peux invoquer cette méthode et passer une expression lambda en argument:
List<String> list = Arrays.asList("a", "b", "c");
sort(list, (a, b) -> a.compareTo(b));
Cela fonctionnera bien.
Mais maintenant, si je rends l'interface non générique et la méthode générique:
interface MyComparable {
public <T extends Comparable<T>> int compare(T obj1, T obj2);
}
public static <T extends Comparable<T>>
void sort(List<T> list, MyComparable comp) {
}
Et invoquer ensuite ceci comme:
List<String> list = Arrays.asList("a", "b", "c");
sort(list, (a, b) -> a.compareTo(b));
Ça ne compile pas. Il montre une erreur dans l'expression lambda en disant:
"La méthode cible est générique"
OK, quand je l'ai compilé avec javac
, l'erreur suivante apparaît:
SO.Java:20: error: incompatible types: cannot infer type-variable(s) T#1
sort(list, (a, b) -> a.compareTo(b));
^
(argument mismatch; invalid functional descriptor for lambda expression
method <T#2>(T#2,T#2)int in interface MyComparable is generic)
where T#1,T#2 are type-variables:
T#1 extends Comparable<T#1> declared in method <T#1>sort(List<T#1>,MyComparable)
T#2 extends Comparable<T#2> declared in method <T#2>compare(T#2,T#2)
1 error
À partir de ce message d'erreur, il semble que le compilateur ne puisse pas déduire les arguments de type. Est-ce le cas? Si oui, alors pourquoi cela se passe-t-il?
J'ai essayé de différentes manières, recherché sur Internet. Ensuite, j'ai trouvé cet article JavaCodeGeeks , qui montre un chemin, alors j'ai essayé:
sort(list, <T extends Comparable<T>>(a, b) -> a.compareTo(b));
ce qui encore ne fonctionne pas, contrairement à ce que cet article prétend qu'il fonctionne. Peut-être que cela fonctionnait dans certaines versions initiales.
Ma question est donc la suivante: existe-t-il un moyen de créer une expression lambda pour une méthode générique? Je peux le faire en utilisant une référence de méthode, en créant une méthode:
public static <T extends Comparable<T>> int compare(T obj1, T obj2) {
return obj1.compareTo(obj2);
}
dans une classe, dites SO
et transmettez-le comme suit:
sort(list, SO::compare);
Vous ne pouvez pas utiliser une expression lambda pour une interface fonctionnelle , si la méthode de l'interface fonctionnelle a des paramètres de type. Voir section §15.27.3 dans JLS8 :
Une expression lambda est compatible [..] avec un type cible T si T est un type d'interface fonctionnelle (§9.8) et que l'expression est congruent avec le type de fonction de [..] T. [..] Une expression lambda est congruent avec un type de fonction si tous les éléments suivants sont vrai:
- Le type de fonction a aucun paramètre de type.
- [..]
En utilisant la référence de méthode, j'ai trouvé un autre moyen de passer l'argument:
List<String> list = Arrays.asList("a", "b", "c");
sort(list, Comparable::<String>compareTo);
Tu veux dire quelque chose comme ca?:
<T,S>(T t, S s)->...
De quel type est ce lambda? Vous ne pouvez pas exprimer cela en Java et par conséquent, vous ne pouvez pas composer cette expression dans une application de fonction et les expressions doivent être composables.
Pour que ce travail soit nécessaire, vous aurez besoin de l’aide de Types Rank2 en Java.
Les méthodes sont autorisées à être génériques, mais vous ne pouvez donc pas les utiliser comme expressions. Ils peuvent toutefois être réduits à une expression lambda en spécifiant tous les types génériques nécessaires avant de pouvoir les transmettre: ClassName::<TypeName>methodName