A continuación se muestran las dos líneas de mi fragmento de código:
List<String> listDevs = Arrays.asList("alvin", "Alchemist", "brutus", "larsen", "jason", "Kevin"); listDevs.sort(Comparator.comparing(String::length)); //This works fine listDevs.sort(String::compareToIgnoreCase); //This works fine
Pero (por experiencia) cuando trato de escribir
listDevs.sort(Comparator.comparing(String::compareToIgnoreCase));
El compilador arroja error
No se puede hacer una referencia estática al método no estático compareToIgnoreCase(String) del tipo String
Similar sucede con el siguiente código
listDevs.sort(Comparator.comparing(String::compareTo));
Entiendo el error y que funciona bien si elimino Comparator.comparing (como se muestra arriba).
Pero mi punto es, ¿cómo funciona esta línea?
listDevs.sort(Comparator.comparing(String::length));
Creo que me estoy perdiendo algo. He leído este hilo. ¿Es este el mismo escenario?
Comparator.comparing
espera una Function
que describa una propiedad comparable de los elementos. Entonces String::length
es suficiente ya que length()
es una propiedad de String
que evalúa una String
a un int
(por eso es preferible comparingInt
aquí).
Por el contrario, String.compareToIgnoreCase
y String.compareTo
son métodos de comparación . Comparan dos objetos String
. Por lo tanto, las referencias a ellos son suficientes donde se espera un Comparator
, pero no donde se espera una Function
de propiedad.
Es como si tuvieras una fábrica que dice: "Dame un motor y construimos un automóvil para ti" y estás tratando de darles un automóvil completo. Si bien ese automóvil existente es válido donde se espera un automóvil, no tiene sentido pasarlo a la fábrica para construir un automóvil.
Desafortunadamente, la implementación actual del compilador es muy mala para informar errores con firmas funcionales. Casi siempre verá mensajes como "No se puede hacer una referencia estática al método no estático..." cuando las firmas no coincidan.
El método de sort
esperaba un Comparator
.
Cuando haces esto, de hecho estás proporcionando uno.
listDevs.sort(Comparator.comparing(String::length));
Lo mismo sucede aquí (pero un poco no intuitivo):
listDevs.sort(String::compareToIgnoreCase) listDevs.sort((left, right) -> left.compareToIgnoreCase(right)); // same thing as above
Esa es exactamente la definición de un Comparator
: toma dos cadenas y devuelve un int.
La línea que dices cómo es que esto funciona: listDevs.sort(Comparator.comparing(String::length));
en realidad es bastante simple.
Comparator.comparing
toma una Function
que transforma su tipo de entrada en algo que es Comparable
. En su caso, toma una String
y devuelve un Integer
; que es comparable.
JLS dice que la declaración de tiempo de compilación de una referencia de método de ReferenceType :: [TypeArguments] Identifier se puede interpretar de diferentes maneras.
Dado un tipo de función objetivo con n parámetros, se identifica un conjunto de métodos potencialmente aplicables:
ReferenceType :: [TypeArguments] El identificador tiene dos aridades diferentes, n y n-1, que se consideran para tener en cuenta la posibilidad de que esta forma se refiera a un método estático o a un método de instancia.
Una expresión de referencia de método de la forma ReferenceType :: [TypeArguments] Identifier se puede interpretar de diferentes maneras. Si Identifier hace referencia a un método de instancia, la expresión lambda implícita tiene un parámetro adicional con el tipo this en comparación con si Identifier hace referencia a un método estático. Es posible que ReferenceType tenga ambos tipos de métodos aplicables, por lo que el algoritmo de búsqueda descrito anteriormente los identifica por separado, ya que existen diferentes tipos de parámetros para cada caso.
Comparator.comparing method accept a Function<T,R extends Comparable<? súper R>> . cuando usa String::compareToIgnoreCase
que informará un error, porque tiene dos parámetros, uno está implícito, este otro es una cadena de comparación del parámetro del método, por lo que es más como una BiFunction<String,String,Integer>
no una Function<String,Integer>
.
BiFunction<String, String, Integer> comparator = String::compareToIgnoreCase; // you can't assign a BiFunction to a Function // because one is incompatiable with another. Function<String,Integer> function = comparator;
El método Stream.sort acepta un Comparator , y Comparator es más como un BiFunction<T,T,Integer>
por lo que es compatible con String::compareToIgnoreCase
. por otro lado, pueden ser intercambiables. por ejemplo:
Comparator<String> primary = String::compareToIgnoreCase; BiFunction<String, String, Integer> comparator1 = primary::compare; Comparator<String> comparator2 = comparator1::apply;
puede usar comparing(String::toLowerCase)
en su lugar, es igual a String::compareToIgnoreCase
, por ejemplo:
// String::compareToIgnoreCase listDevs.sort(String::compareToIgnoreCase); // comparing(String::toLowerCase) listDevs.sort(comparing(String::toLowerCase))