streams recorrer procesamiento parte metodos lista funciones expresiones example ejemplo datos con collection anonimas java java-8 java-stream

procesamiento - recorrer lista java 8



.Min() y.max() de Java 8 stream: ¿por qué compila esto? (5)

Aparte de la información proporcionada por David M. Lloyd, se podría agregar que el mecanismo que permite esto se denomina tipificación de objetivos .

La idea es que el tipo que el compilador asigna a las expresiones lambda o las referencias de un método no depende solo de la expresión en sí, sino también de dónde se usa.

El destino de una expresión es la variable a la que se asigna su resultado o el parámetro al que se pasa su resultado.

A las expresiones Lambda y las referencias de método se les asigna un tipo que coincide con el tipo de su objetivo, si se puede encontrar dicho tipo.

Consulte la sección Inferencia de tipos en el Tutorial de Java para obtener más información.

Nota: esta pregunta se origina a partir de un enlace muerto que era una pregunta anterior de SO, pero aquí va ...

Vea este código ( nota: sé que este código no "funcionará" y que se debe usar Integer::compare , simplemente lo extraje de la pregunta enlazada ):

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());

De acuerdo con el javadoc de .min() y .max() , el argumento de ambos debe ser un Comparator . Sin embargo, aquí las referencias de los métodos son métodos estáticos de la clase Integer .

Entonces, ¿por qué se compila esto?


Déjame explicarte lo que está pasando aquí, ¡porque no es obvio!

Primero, .max() acepta una instancia de Comparator para que los elementos del flujo puedan compararse entre sí para encontrar el mínimo o el máximo, en un orden óptimo del que no deba preocuparse demasiado.

Entonces, la pregunta es, por supuesto, ¿por qué se acepta Integer::max ? ¡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 se puede implementar automáticamente mediante cualquier lambda, o referencia de método, cuya firma de método coincida con el método de la interfaz. Así que examinando la interfaz del Comparator (versión simple):

public Comparator<T> { T compare(T o1, T o2); }

Si un método busca un Comparator<Integer> , entonces esencialmente busca esta firma:

int xxx(Integer o1, Integer o2);

Uso "xxx" porque el nombre del método no se usa para propósitos de coincidencia .

Por lo tanto, tanto Integer.min(int a, int b) como Integer.max(int a, int b) están lo suficientemente cerca como para que el autoboxing permita que esto aparezca como un Comparator<Integer> en un contexto de método.


Esto funciona porque Integer::min resuelve en una implementación de la interfaz Comparator<Integer> del Comparator<Integer> .

La referencia del método de Integer::min resuelve en Integer.min(int a, int b) , se resuelve en IntBinaryOperator y, presumiblemente, el autoboxing ocurre en algún lugar, lo que lo convierte en un BinaryOperator<Integer> .

Y los métodos min() resp max() del Stream<Integer> piden que se implemente la interfaz Comparator<Integer> .
Ahora esto se resuelve con el método único Integer compareTo(Integer o1, Integer o2) . Que es de tipo BinaryOperator<Integer> .

Y así, la magia ha ocurrido ya que ambos métodos son un BinaryOperator<Integer> .


Tuve un error con una matriz obteniendo el máximo y el mínimo, así que mi solución fue:

int max = Arrays.stream(arrayWithInts).max().getAsInt(); int min = Arrays.stream(arrayWithInts).min().getAsInt();


Comparator es una interfaz funcional , y Integer::max cumple con esa interfaz (después de tomar en cuenta el autoboxing / unboxing). Toma dos valores int y devuelve un int , tal como lo esperaría un Comparator<Integer> (a), de nuevo, entornando los ojos para ignorar la diferencia Integer / int.

Sin embargo, no esperaría que hiciera lo correcto, dado que Integer.max no cumple con la semántica de Comparator.compare . Y de hecho no funciona realmente en general. Por ejemplo, haz un pequeño cambio:

for (int i = 1; i <= 20; i++) list.add(-i);

... y ahora el valor max es -20 y el valor min es -1.

En su lugar, ambas llamadas deben usar Integer::compare :

System.out.println(list.stream().max(Integer::compare).get()); System.out.println(list.stream().min(Integer::compare).get());