Supongamos que tengo una interfaz genérica:
interface MyComparable<T extends Comparable<T>> {
public int compare(T obj1, T obj2);
}
Y un método sort
:
public static <T extends Comparable<T>>
void sort(List<T> list, MyComparable<T> comp) {
// sort the list
}
Puedo invocar este método y pasar una expresión lambda como argumento:
List<String> list = Arrays.asList("a", "b", "c");
sort(list, (a, b) -> a.compareTo(b));
Eso funcionará bien.
Pero ahora, si hago la interfaz no genérica y el método genérico:
interface MyComparable {
public <T extends Comparable<T>> int compare(T obj1, T obj2);
}
public static <T extends Comparable<T>>
void sort(List<T> list, MyComparable comp) {
}
Y luego invoca esto como:
List<String> list = Arrays.asList("a", "b", "c");
sort(list, (a, b) -> a.compareTo(b));
No se compila. Muestra un error en la expresión lambda que dice:
"El método de destino es genérico"
OK, cuando lo compilé usando javac
, muestra el siguiente error:
SO.java:20: error: incompatible types: cannot infer type-variable(s) T#1
sort(list, (a, b) -> a.compareTo(b));
^
(argument mismatch; invalid functional descriptor for lambda expression
method <T#2>(T#2,T#2)int in interface MyComparable is generic)
where T#1,T#2 are type-variables:
T#1 extends Comparable<T#1> declared in method <T#1>sort(List<T#1>,MyComparable)
T#2 extends Comparable<T#2> declared in method <T#2>compare(T#2,T#2)
1 error
A partir de este mensaje de error, parece que el compilador no puede inferir los argumentos de tipo. ¿Es ese el caso? Si es así, ¿por qué está sucediendo así?
Probé de varias formas, busqué en Internet. Luego encontré este artículo de JavaCodeGeeks , que muestra una forma, así que probé:
sort(list, <T extends Comparable<T>>(a, b) -> a.compareTo(b));
que de nuevo no funciona, al contrario de lo que ese artículo afirma que funciona. Podría ser posible que solía funcionar en algunas versiones iniciales.
Entonces mi pregunta es: ¿hay alguna forma de crear una expresión lambda para un método genérico? Sin embargo, puedo hacer esto usando una referencia de método, creando un método:
public static <T extends Comparable<T>> int compare(T obj1, T obj2) {
return obj1.compareTo(obj2);
}
en alguna clase decir SO
, y pasarlo como:
sort(list, SO::compare);
Usando la referencia del método, encontré otra forma de pasar el argumento:
fuente
Simplemente señale al compilador la versión adecuada del Comparador genérico con
(Comparator<String>)
Entonces la respuesta será
sort(list, (Comparator<String>)(a, b) -> a.compareTo(b));
fuente
incompatible types: java.util.Comparator<java.lang.String> cannot be converted to MyComparable
yMyComparable
no es genérico (sin tipo), por(MyComparable<String>)
lo que tampoco funcionaríaCompartor
?¿Te refieres a algo como esto ?:
¿De qué tipo es esta lambda? No podría expresar eso en Java y, por lo tanto, no puede componer esta expresión en una aplicación de función y las expresiones deben ser componibles.
Para que esto funcione, necesitaría soporte para los tipos Rank2 en Java.
Se permite que los métodos sean genéricos pero, por lo tanto, no se pueden usar como expresiones. Sin embargo, se pueden reducir a una expresión lambda especializando todos los tipos genéricos necesarios antes de poder pasarlos:
ClassName::<TypeName>methodName
fuente
"Of what type is this lambda? You couldn't express that in Java..."
El tipo se inferiría utilizando el contexto, al igual que con cualquier otro lambda. El tipo de lambda no se expresa explícitamente en el propio lambda.