Nota: esta pregunta se origina en un enlace inactivo que era una pregunta SO anterior, pero aquí va ...
Vea este código ( nota: sé que este código no "funcionará" y que Integer::comparedebería usarse, lo acabo de extraer de la pregunta vinculada ):
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());
Según el javadoc de .min()y .max(), el argumento de ambos debería ser a Comparator. Sin embargo, aquí las referencias de métodos son a métodos estáticos de la Integerclase.
Entonces, ¿por qué esto compila en absoluto?
                    
                        java
                                java-8
                                java-stream
                                
                    
                    
                        fge
fuente
                
                fuente

Integer::comparelugar deInteger::maxyInteger::min.Integerno son métodos deComparator.Respuestas:
Déjame explicarte lo que está sucediendo aquí, ¡porque no es obvio!
Primero,
Stream.max()acepta una instancia deComparatormodo que los elementos en la secuencia se puedan comparar entre sí para encontrar el mínimo o el máximo, en un orden óptimo del que no tenga que preocuparse demasiado.Entonces la pregunta es, por supuesto, ¿por qué se
Integer::maxacepta? ¡Después de todo no es un comparador!La respuesta está en la forma en que la nueva funcionalidad lambda funciona en Java 8. Se basa en un concepto que se conoce informalmente como interfaces de "método abstracto único" o interfaces "SAM". La idea es que cualquier interfaz con un método abstracto puede implementarse automáticamente por cualquier lambda, o referencia de método, cuya firma de método coincida con el método en la interfaz. Entonces, examinando la
Comparatorinterfaz (versión simple):Si un método está buscando un
Comparator<Integer>, entonces esencialmente está buscando esta firma:Yo uso "xxx" porque el nombre del método no se utiliza con fines coincidentes .
Por lo tanto, tanto
Integer.min(int a, int b)yInteger.max(int a, int b)son tan cerca que autoboxing permitirá que esto aparezca como unaComparator<Integer>en un contexto método.fuente
list.stream().mapToInt(i -> i).max().get()..getAsInt()lugar deget()sin embargo, ya que se trata de unOptionalInt.max()función!Comparatordocumentación podemos ver que está decorada con la anotación@FunctionalInterface. Este decorador es la magia que permiteInteger::maxyInteger::minse convierte en aComparator.@FunctionalInterfacees principalmente para fines de documentación, ya que el compilador puede hacerlo felizmente con cualquier interfaz con un solo método abstracto.Comparatores una interfaz funcional yInteger::maxcumple con esa interfaz (después de tomar en cuenta el autoboxing / unboxing). Toma dosintvalores y devuelve unint- tal como esperaría unComparator<Integer>a (nuevamente, entrecerrando los ojos para ignorar la diferencia Entero / int).Sin embargo, no esperaría que hiciera lo correcto, dado que
Integer.maxno cumple con la semántica deComparator.compare. Y, de hecho, en realidad no funciona en general. Por ejemplo, haga un pequeño cambio:... y ahora el
maxvalor es -20 y elminvalor es -1.En cambio, ambas llamadas deben usar
Integer::compare:fuente
Comparator<Integer>tendríaint compare(Integer, Integer)... no es alucinante que Java permita una referencia de métodoint max(int, int)para convertir a eso ...Integer::max? Desde su perspectiva, usted pasó una función que cumplió con sus especificaciones, eso es todo lo que realmente puede continuar.Comparator.compare. Debería devolver unenumde{LessThan, GreaterThan, Equal}, no unint. De esa manera, la interfaz funcional no coincidiría realmente y obtendría un error de compilación. IOW: la firma de tipo deComparator.compareno captura adecuadamente la semántica de lo que significa comparar dos objetos y, por lo tanto, otras interfaces que no tienen absolutamente nada que ver con la comparación de objetos tienen accidentalmente la misma firma de tipo.Esto funciona porque
Integer::minresuelve una implementación de laComparator<Integer>interfaz.La referencia de método de
Integer::minresuelveInteger.min(int a, int b), resuelveIntBinaryOperatory presumiblemente autoboxing ocurre en algún lugar, lo que lo convierte en unBinaryOperator<Integer>.Y los métodos de
min()respuesta solicitan la implementación de la interfaz. Ahora esto se resuelve con el método único . Cuál es de tipo .max()Stream<Integer>Comparator<Integer>Integer compareTo(Integer o1, Integer o2)BinaryOperator<Integer>Y así, la magia ha sucedido ya que ambos métodos son a
BinaryOperator<Integer>.fuente
Integer::minimplementaComparable. No es un tipo que pueda implementar nada. Pero se evalúa en un objeto que implementaComparable.Comparator<Integer>es una interfaz de método abstracto único (también conocido como "funcional") yInteger::mincumple con su contrato, por lo que la lambda se puede interpretar como esto. No sé cómo ves que BinaryOperator entra en juego aquí (o IntBinaryOperator, tampoco): no hay una relación de subtipo entre eso y Comparator.Además de la información proporcionada por David M. Lloyd, se podría agregar que el mecanismo que permite esto se llama tipificación de objetivos .
La idea es que el tipo que el compilador asigna a las expresiones lambda o referencias de un método no depende solo de la expresión en sí, sino también de dónde se usa.
El objetivo de una expresión es la variable a la que se asigna su resultado o el parámetro al que se pasa su resultado.
Las expresiones Lambda y las referencias de métodos tienen asignado un tipo que coincide con el tipo de su destino, si se puede encontrar dicho tipo.
Consulte la sección Inferencia de tipo en el Tutorial de Java para obtener más información.
fuente
Tuve un error con una matriz obteniendo el máximo y el mínimo, por lo que mi solución fue:
fuente