Java 8 stream .min() et .max(): pourquoi cette compilation?
Remarque: cette question provient d'un lien mort, qui a été un précédent DONC, la question, mais voilà...
Voir ce code (note: je sais que ce code ne marche pas et que Integer::compare
doit être utilisé -- je viens de l'extraction 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 la javadoc de .min()
et .max()
, l'argument des deux doit être un Comparator
. Pourtant, ici, la méthode des références à des méthodes statiques de la Integer
classe.
Alors, pourquoi est-ce de la compilation à tous?
- Notez que cela ne fonctionne pas correctement, il doit être à l'aide de
Integer::compare
au lieu deInteger::max
etInteger::min
. - Je sais que; note comment je l'ai dit avant que le code de l'extrait de "je sais, c'est absurde"
- Je n'étais pas en train de vous corriger, je dis aux gens en général. Vous avez fait le bruit comme si vous pensiez que la partie qui est absurde, c'est que les méthodes de
Integer
ne sont pas des méthodes deComparator
.
Vous devez vous connecter pour publier un commentaire.
Laissez-moi vous expliquer ce qui se passe ici, car il n'est pas évident!
D'abord,
Stream.max()
accepte une instance deComparateur
de sorte que les éléments dans le flux de données peuvent être comparés les uns contre les autres pour trouver le minimum ou le maximum, dans certains optimale afin que vous n'avez pas besoin de trop s'inquiéter.Donc la question est, bien sûr, pourquoi est -
Integer::max
accepté? Après tout ce n'est pas un comparateur de!La réponse est dans la façon dont la nouvelle lambda fonctionnalité fonctionne dans Java 8. Il s'appuie sur un concept qui est officieusement connu comme "la seule méthode abstraite d '"interfaces", ou "SAM" interfaces. L'idée est que toute l'interface avec une méthode abstraite peut être automatiquement mis en œuvre par n'importe quel lambda - ou de la méthode de référence - dont la signature de la méthode est un match pour une méthode sur l'interface. Si l'examen de la
Comparateur
interface (version simple):Si une méthode est à la recherche d'un
Comparator<Integer>
, puis c'est essentiellement à la recherche de cette signature:- Je utiliser "xxx" parce que le nom de la méthode n'est pas utilisée à 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 permettra à ce à apparaître comme unComparator<Integer>
dans une méthode de contexte.list.stream().mapToInt(i -> i).max().get()
..getAsInt()
au lieu deget()
si, comme vous faites affaire avec unOptionalInt
.max()
fonction!Comparator
de la documentation, nous pouvons voir que c'est décorée avec l'annotation@FunctionalInterface
. Ce décorateur est la magie qui permetInteger::max
etInteger::min
être converti en unComparator
.@FunctionalInterface
est principalement à des fins de documentation seulement, comme le compilateur peut heureusement faire cela avec n'importe quelle interface avec une seule méthode abstraite.Comparator
est un interface fonctionnelle, etInteger::max
est conforme à cette interface (après l'autoboxing/unboxing est pris en compte). Il prend deuxint
valeurs et qui renvoie unint
- tout comme vous vous attendez à unComparator<Integer>
(de nouveau, il plisse les yeux pour ignorer l'Entier/int différence).Cependant, je ne m'attends pas à faire la bonne chose, étant donné que
Integer.max
n'est pas conforme avec la sémantique deComparator.compare
. Et en effet ça ne fonctionne pas vraiment en général. Par exemple, effectuer une petite modification:... et maintenant le
max
valeur est de -20 et lemin
valeur est -1.Au lieu de cela, les deux appels doivent utiliser
Integer::compare
:Comparator<Integer>
auraitint compare(Integer, Integer)
... ce n'est pas ahurissant que Java permet à une méthode de référence deint max(int, int)
convertir à ce que...Integer::max
? De son point de vue que vous avez passé dans une fonction qui a rencontré son cahier des charges, c'est tout ce qu'il peut vraiment aller sur.Comparator.compare
. Il doit retourner unenum
de{LessThan, GreaterThan, Equal}
, pas unint
. De cette façon, l'interface fonctionnelle ne correspondent pas réellement et vous obtiendrez une erreur de compilation. OIE: la signature d'un type deComparator.compare
n'a pas suffisamment de capturer la sémantique de ce que cela signifie pour comparer deux objets, et donc d'autres interfaces qui n'ont absolument rien à faire avec des objets de comparaison accidentellement ont le même type de signature.Cela fonctionne parce que
Integer::min
résout à une mise en œuvre de laComparator<Integer>
interface.La méthode de référence de
Integer::min
décide deInteger.min(int a, int b)
, résolu àIntBinaryOperator
, et sans doute l'autoboxing se produit quelque part faire unBinaryOperator<Integer>
.Et la
min()
respmax()
méthodes de laStream<Integer>
demander à laComparator<Integer>
interface pour être mis en œuvre.Maintenant, cela résout à la méthode unique
Integer compareTo(Integer o1, Integer o2)
. Qui est de typeBinaryOperator<Integer>
.Et donc la magie s'est passé que les deux méthodes sont un
BinaryOperator<Integer>
.Integer::min
implémenteComparable
. Il n'est pas un type qui peut mettre en œuvre quoi que ce soit. Mais il est évalué dans un objet qui implémenteComparable
.Comparator<Integer>
est un simple résumé de la méthode (aka "fonctionnelle") de l'interface, etInteger::min
remplit son contrat, de sorte que le lambda peut être interprété comme cela. Je ne sais pas comment vous voyez BinaryOperator venir ici en jeu (ou IntBinaryOperator, soit) – il n'y a pas de relation de sous-typage entre ça et le Comparateur.En dehors de l'information donnée par M. David Lloyd, on pourrait ajouter que le mécanisme qui permet à ce qui est appelé cible en tapant.
L'idée est que le type, le compilateur attribue les expressions lambda ou une méthode références ne dépend pas seulement de l'expression elle-même, mais aussi sur l'endroit où il est utilisé.
La cible d'une expression est la variable à laquelle son résultat est assigné ou le paramètre auquel son résultat est transmis.
Les expressions Lambda et de la méthode références sont affectés à un type qui correspond au type de leur cible, si ce type peut être trouvé.
Voir le L'Inférence de Type section dans le Tutoriel Java pour plus d'informations.
J'ai eu une erreur avec un tableau d'obtenir le max et le min donc ma solution a été: