Remarque: cette question provient d'un lien mort qui était une question SO précédente, mais voici ...
Voir ce code ( note: je sais que ce code ne "fonctionnera" pas et qu'il Integer::compare
devrait être utilisé - je viens de l'extraire de la question liée ):
final ArrayList <Integer> list
= IntStream.rangeClosed(1, 20).boxed().collect(Collectors.toList());
System.out.println(list.stream().max(Integer::max).get());
System.out.println(list.stream().min(Integer::min).get());
Selon le javadoc de .min()
et .max()
, l'argument des deux devrait être a Comparator
. Pourtant, ici, les références de méthode sont des méthodes statiques de la Integer
classe.
Alors, pourquoi cela se compile-il?
Integer::compare
place deInteger::max
etInteger::min
.Integer
ne sont pas des méthodes deComparator
.Réponses:
Permettez-moi d'expliquer ce qui se passe ici, car ce n'est pas évident!
Tout d'abord,
Stream.max()
accepte une instance deComparator
sorte que les éléments du flux puissent être comparés les uns aux autres pour trouver le minimum ou le maximum, dans un ordre optimal dont vous n'avez pas trop à vous soucier.La question est donc, bien sûr, pourquoi est-il
Integer::max
accepté? Après tout, ce n'est pas un comparateur!La réponse réside dans la façon dont la nouvelle fonctionnalité lambda fonctionne en Java 8. Elle repose sur un concept qui est connu de manière informelle sous le nom d'interfaces «méthode abstraite unique» ou interfaces «SAM». L'idée est que toute interface avec une méthode abstraite peut être implémentée automatiquement par n'importe quelle lambda - ou référence de méthode - dont la signature de méthode correspond à la seule méthode de l'interface. Examinons donc l'
Comparator
interface (version simple):Si une méthode recherche un
Comparator<Integer>
, elle recherche essentiellement cette signature:J'utilise "xxx" car le nom de la méthode n'est pas utilisé à des fins de correspondance .
Par conséquent, les deux
Integer.min(int a, int b)
etInteger.max(int a, int b)
sont suffisamment proches pour que l'autoboxing permette que cela apparaisse comme unComparator<Integer>
dans un contexte de méthode.la source
list.stream().mapToInt(i -> i).max().get()
..getAsInt()
plutôtget()
que, car vous avez affaire à unOptionalInt
.max()
fonction!Comparator
documentation, nous pouvons voir qu'elle est décorée avec l'annotation@FunctionalInterface
. Ce décorateur est la magie qui permetInteger::max
etInteger::min
se transforme enComparator
.@FunctionalInterface
est principalement à des fins de documentation uniquement, car le compilateur peut le faire avec n'importe quelle interface avec une seule méthode abstraite.Comparator
est une interface fonctionnelle , etInteger::max
est conforme à cette interface (après prise en compte de l'autoboxing / unboxing). Il prend deuxint
valeurs et renvoie unint
- tout comme vous vous attendez à unComparator<Integer>
(encore une fois, loucher pour ignorer la différence Integer / int).Cependant, je ne m'attendrais pas à ce qu'il fasse la bonne chose, étant donné que
Integer.max
cela ne respecte pas la sémantique deComparator.compare
. Et en effet, cela ne fonctionne pas vraiment en général. Par exemple, apportez une petite modification:... et maintenant la
max
valeur est -20 et lemin
valeur est -1.Au lieu de cela, les deux appels doivent utiliser
Integer::compare
:la source
Comparator<Integer>
auraitint compare(Integer, Integer)
... ce n'est pas ahurissant que Java permette une référence de méthode deint max(int, int)
se convertir à cela ...Integer::max
? De son point de vue, vous avez passé dans une fonction qui répondait à ses spécifications, c'est tout ce qu'il peut vraiment continuer.Comparator.compare
. Il doit renvoyer unenum
de{LessThan, GreaterThan, Equal}
, pas unint
. De cette façon, l'interface fonctionnelle ne correspondrait pas réellement et vous obtiendriez une erreur de compilation. IOW: la signature de type deComparator.compare
ne capture pas correctement la sémantique de ce que signifie comparer deux objets, et donc d'autres interfaces qui n'ont absolument rien à voir avec la comparaison accidentelle d'objets ont la même signature de type.Cela fonctionne car
Integer::min
résout une implémentation de l'Comparator<Integer>
interface.La référence de méthode de
Integer::min
résoutInteger.min(int a, int b)
, résoluIntBinaryOperator
et vraisemblablement autoboxe se produit quelque part, ce qui en fait unBinaryOperator<Integer>
.Et les méthodes
min()
respmax()
deStream<Integer>
demander l'Comparator<Integer>
interface à implémenter.Maintenant, cela se résout à la méthode unique
Integer compareTo(Integer o1, Integer o2)
. Qui est de typeBinaryOperator<Integer>
.Et donc la magie s'est produite car les deux méthodes sont a
BinaryOperator<Integer>
.la source
Integer::min
met en œuvreComparable
. Ce n'est pas un type qui peut implémenter quoi que ce soit. Mais il est évalué en un objet qui implémenteComparable
.Comparator<Integer>
est une interface à méthode abstraite unique (aka "fonctionnelle"), etInteger::min
remplit son contrat, donc le lambda peut être interprété comme ceci. Je ne sais pas comment vous voyez BinaryOperator entrer en jeu ici (ou IntBinaryOperator, soit) - il n'y a pas de relation de sous-typage entre cela et Comparator.Outre les informations fournies par David M. Lloyd, on pourrait ajouter que le mécanisme qui permet cela s'appelle le typage cible .
L'idée est que le type que le compilateur assigne à une expression lambda ou à une référence de méthode ne dépend pas seulement de l'expression elle-même, mais aussi de l'endroit où elle est utilisée.
La cible d'une expression est la variable à laquelle son résultat est affecté ou le paramètre auquel son résultat est transmis.
Les expressions lambda et les références de méthode se voient attribuer un type qui correspond au type de leur cible, si un tel type peut être trouvé.
Consultez la section Inférence de type dans le didacticiel Java pour plus d'informations.
la source
J'ai eu une erreur avec un tableau obtenant le max et le min donc ma solution était:
la source