Suponha que eu tenha uma interface genérica:
interface MyComparable<T extends Comparable<T>> {
public int compare(T obj1, T obj2);
}
E um método sort
:
public static <T extends Comparable<T>>
void sort(List<T> list, MyComparable<T> comp) {
// sort the list
}
Posso invocar este método e passar uma expressão lambda como argumento:
List<String> list = Arrays.asList("a", "b", "c");
sort(list, (a, b) -> a.compareTo(b));
Isso vai funcionar bem.
Mas agora, se eu tornar a interface não genérica e o 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) {
}
E então invoque isso como:
List<String> list = Arrays.asList("a", "b", "c");
sort(list, (a, b) -> a.compareTo(b));
Não compila. Ele mostra um erro na expressão lambda dizendo:
"O método alvo é genérico"
Ok, quando compilei usando javac
, ele mostra o seguinte erro:
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 dessa mensagem de erro, parece que o compilador não é capaz de inferir os argumentos de tipo. É esse o caso? Se sim, então por que está acontecendo assim?
Tentei de várias formas, pesquisei na internet. Então eu encontrei este artigo JavaCodeGeeks , que mostra um caminho, então tentei:
sort(list, <T extends Comparable<T>>(a, b) -> a.compareTo(b));
que novamente não funciona, ao contrário do que aquele artigo afirma que funciona. Pode ser que funcionasse em algumas compilações iniciais.
Portanto, minha pergunta é: Existe alguma maneira de criar uma expressão lambda para um método genérico? Posso fazer isso usando uma referência de método, porém, criando um método:
public static <T extends Comparable<T>> int compare(T obj1, T obj2) {
return obj1.compareTo(obj2);
}
em alguma aula diga SO
e passe como:
sort(list, SO::compare);
Usando a referência de método, encontrei outra maneira de passar o argumento:
fonte
Basta apontar para o compilador a versão adequada do Comparator genérico com
(Comparator<String>)
Então a resposta será
sort(list, (Comparator<String>)(a, b) -> a.compareTo(b));
fonte
incompatible types: java.util.Comparator<java.lang.String> cannot be converted to MyComparable
eMyComparable
não é genérico (nenhum tipo), portanto(MyComparable<String>)
também não funcionariaCompartor
?Você quer dizer algo assim ?:
De que tipo é este lambda? Você não pode expressar isso em Java e, portanto, não pode compor essa expressão em um aplicativo de função e as expressões precisam ser compostas.
Para que isso funcione, você precisa de suporte para Tipos Rank2 em Java.
Os métodos podem ser genéricos, mas, portanto, você não pode usá-los como expressões. No entanto, eles podem ser reduzidos à expressão lambda, especializando todos os tipos genéricos necessários antes que você possa passá-los:
ClassName::<TypeName>methodName
fonte
"Of what type is this lambda? You couldn't express that in Java..."
O tipo seria inferido usando o contexto, assim como com qualquer outro lambda. O tipo de lambda não é explicitamente expresso no próprio lambda.