Estoy tratando de entender por qué el compilador no puede resolver la llamada al método de bar . Esperaría que bar(Xyz::new) seleccione siempre bar(Supplier) ya que bar(T extends Xyz) nunca puede coincidir debido al límite superior en Xyz .
public <T extends Xyz> void foo(T s) {} public <T extends Xyz> void bar(T s) {} public <T extends Xyz> void bar(Supplier<T> s) {} public void example() { foo(Xyz::new); // not valid (does not extend Xyz) bar((Supplier<Xyz>) Xyz::new); // valid (explicitly a Supplier) bar(Xyz::new); // ambiguous - but only one method is valid? } public static class Xyz {} Si bar(T) no es aplicable, incluso cuando está solo (como se muestra con foo(T) ), entonces seguramente la única opción es bar(Supplier) haciendo de esto una sobrecarga no ambigua.
¿Por qué la llamada de bar es ambigua, especialmente cuando las llamadas foo y bar(T) no son resoluciones válidas en sí mismas?
Ejemplo ejecutable del código anterior: https://www.jdoodle.com/ia/kqP
Tiene razón en que un compilador más inteligente debería poder resolver esto sin ambigüedades.
La forma en que Java resuelve las invocaciones de métodos es compleja . Estádefinido por el JLS , y lo hago 7500 palabras simplemente para determinar cómo resolver un método. Pegado en un editor de texto, tenía 15 páginas.
El enfoque general es:
No entiendo ni cerca de todos los detalles y cómo se relaciona con su caso específico. Si desea sumergirse en él, ya he vinculado la especificación completa. Esperemos que esta explicación sea lo suficientemente buena para sus propósitos:
La ambigüedad se determina en el paso 2.6, pero todavía hay una verificación adicional de adecuación en el paso 3. Su método foo debe estar fallando en el paso 3. Su método bar nunca llega tan lejos porque el compilador aún considera que ambos métodos son posibilidades válidas. Un ser humano puede tomar la determinación de que la inadecuación resuelve la ambigüedad, pero ese no es el orden en que el compilador hace las cosas. Solo podía especular por qué: el rendimiento podría ser un factor.
Su código está operando en la intersección de los genéricos, la sobrecarga y las referencias de métodos, los cuales se introdujeron en diferentes momentos; no me sorprende enormemente que el compilador tenga problemas.
Su problema es principalmente un problema de inferencia de tipo que un problema de método ambiguo:
public <T extends Xyz> void bar(T s) {} // bar(Xyz) public void bar(String s) {} public <T extends Zyx> void bar(T s) {} // bar(Zyx) public <T extends Xyz> void bar(Supplier<T> s) {} public static class Xyz {} public static class Zyx {}Si utiliza:
bar(new Xyz()); // ok bar("a"); // ok bar(new Zyx()); // ok bar((Supplier<Xyz>) Xyz::new); // ok bar(Xyz::new); // ambiguous Obtiene este error (probado con Java 17) que no se trata de la lambda, sino del tipo T : no se puede inferir tipo-variable (s) T
both method <T#1>bar(T#1) in Example and method <T#2>bar(Supplier<T#2>) in Example match where T#1,T#2 are type-variables: T#1 extends Zyx declared in method <T#1>bar(T#1) T#2 extends Xyz declared in method <T#2>bar(Supplier<T#2>) Example.java:18: error: incompatible types: cannot infer type-variable(s) TJava no es lo suficientemente inteligente como para encontrar el tipo T concreto en este caso, y tienes que ayudarlo:
Example.<Xyz>bar(Xyz::new);Traté de buscar en el JLS, impulsado por la respuesta de Michael, y la sección que debería responder mejor a su pregunta es la 18.5.1. Inferencia de la aplicabilidad de la invocación .
Tuve el mismo tipo de errores que ocurrían con frecuencia con Java 7 y Colecciones:
public static <T extends Zyx> void bar(java.util.List<T> s) {} // bar(Zyx) public static <T extends Zyx> void bar(T s) {} // bar(List) bar(new Zyx()); bar(java.util.Collections.emptyList());Lo peor de todo es que Eclipse no tenía problemas, mientras que javac fallaba.
Supongo que en el caso de las lambdas, el compilador no infiere el tipo T del "Xyz".