La referencia es ambigua con los generics

Estoy teniendo un caso bastante complicado aquí con generics y sobrecarga de métodos. Mira esta clase de ejemplo:

public class Test { public  void setValue(Parameter parameter, T value) { } public  void setValue(Parameter parameter, Field value) { } public void test() { // This works perfectly.  is bound to String // ambiguity between setValue(.., String) and setValue(.., Field) // is impossible as String and Field are incompatible Parameter p1 = getP1(); Field f1 = getF1(); setValue(p1, f1); // This causes issues.  is bound to Object // ambiguity between setValue(.., Object) and setValue(.., Field) // is possible as Object and Field are compatible Parameter p2 = getP2(); Field f2 = getF2(); setValue(p2, f2); } private Parameter getP1() {...} private Parameter getP2() {...} private Field getF1() {...} private Field getF2() {...} } 

El ejemplo anterior se comstack perfectamente en Eclipse (Java 1.6), pero no con el comando Ant javac (o con el comando javac del JDK), donde recibo este tipo de mensaje de error en la segunda invocación de setValue :

la referencia a setValue es ambigua, tanto el método setValue (org.jooq.Parameter, T) en Test y el método setValue (org.jooq.Parameter, org.jooq.Field) en Test match

De acuerdo con la especificación y, a mi entender, cómo funciona el comstackdor de Java, siempre se debe elegir el método más específico: http://java.sun.com/docs/books/jls/third_edition/html/expressions.html#20448

En cualquier caso, incluso si está vinculado a Object , lo que hace que ambos métodos setValue candidatos aceptables para la invocación, el que tiene el parámetro Field siempre parece ser más específico. Y funciona en Eclipse, pero no con el comstackdor de JDK.

ACTUALIZAR :

De esta manera, funcionaría tanto en Eclipse como con el comstackdor JDK (con advertencias de tipos de materias primas, por supuesto). Entiendo que las reglas especificadas en las especificaciones son bastante especiales, cuando los generics están involucrados. Pero encuentro esto bastante confuso:

  public  void setValue(Parameter parameter, Object value) { } // Here, it's easy to see that this method is more specific public  void setValue(Parameter parameter, Field value) { } 

ACTUALIZACIÓN 2 :

Incluso con generics, puedo crear esta solución alternativa donde evito que el tipo se setValue a Object en setValue invocation time, al agregar un setValue0 indirecto adicional no ambiguo llamado setValue0 . Esto me hace pensar que la unión de T a Object es realmente lo que está causando todos los problemas aquí:

  public  void setValue(Parameter parameter, T value) { } public  void setValue(Parameter parameter, Field value) { } public  void setValue0(Parameter parameter, Field value) { // This call wasn't ambiguous in Java 7 // It is now ambiguous in Java 8! setValue(parameter, value); } public void test() { Parameter p2 = p2(); Field f2 = f2(); setValue0(p2, f2); } 

¿Estoy malentendiendo algo aquí? ¿Hay un error de comstackción conocido relacionado con esto? ¿O hay una configuración de solución / comstackdor para ayudarme?

Seguir:

Para aquellos interesados, he presentado un informe de error tanto a Oracle como a Eclipse. Oracle ha aceptado el error, hasta ahora, ¡Eclipse lo ha analizado y lo ha rechazado! Parece que mi intuición es correcta y este es un error en javac

  • http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=7031404
  • https://bugs.eclipse.org/bugs/show_bug.cgi?id=340506
  • https://bugs.eclipse.org/bugs/show_bug.cgi?id=469014 (un nuevo problema en Eclipse Mars)

JDK tiene razón. El segundo método no es más específico que el primero. De JLS3 # 15.12.2.5

“La intuición informal es que un método es más específico que otro si cualquier invocación manejada por el primer método podría pasarse a la otra sin un error de tipo de tiempo de comstackción”.

Este no es claramente el caso aquí. Hice hincapié en cualquier invocación . La propiedad de que un método sea más específico que otro depende puramente de los dos métodos; no cambia por invocación.

Análisis formal de su problema: ¿m2 es más específico que m1?

 m1:  void setValue(Parameter parameter, R value) m2:  void setValue(Parameter parameter, Field value) 

Primero, el comstackdor necesita inferir R de las restricciones iniciales:

 Parameter < < Parameter Field < < R 

El resultado es R=V , por reglas de inferencia en 15.12.2.7

Ahora sustituimos R y verificamos las relaciones de subtipo

 Parameter <: Parameter Field <: V 

La 2da línea no se cumple, por reglas de subtipificación en 4.10.2. Entonces, m2 no es más específico que m1.

V no es un Object en este análisis; el análisis considera todos los valores posibles de V

Sugeriría usar diferentes nombres de método. La sobrecarga nunca es una necesidad.


Esto parece ser un error importante en Eclipse. La especificación indica claramente que las variables de tipo no se sustituyen en este paso. Eclipse aparentemente escribe primero la sustitución de variables, luego verifica la relación de especificidad del método.

Si tal comportamiento es más "sensible" en algunos ejemplos, no está en otros ejemplos. Decir,

 m1:  void check(List list, T obj) { print("1"); } m2:  void check(List list, T num) { print("2"); } void test() check( new ArrayList(), new Integer(0) ); 

"Intuitivamente", y formalmente por espec., M2 es más específico que m1, y la prueba imprime "2". Sin embargo, si la sustitución T=Integer se hace primero, ¡los dos métodos se vuelven idénticos!


para la actualización 2

 m1:  void setValue(Parameter parameter, R value) m2:  void setValue(Parameter parameter, Field value) m3:  void setValue2(Parameter parameter, Field value) s4: setValue(parameter, value) 

Aquí, m1 no es aplicable para la invocación de método s4, por lo que m2 es la única opción.

Según 15.12.2.2, para ver si m1 es aplicable para s4, primero, se lleva a cabo la inferencia de tipo, hasta la conclusión de que R = T; luego comprobamos que Ai :< Si , lo que lleva a Field <: T , que es falso.

Esto es consistente con el análisis anterior: si m1 es aplicable a s4, cualquier invocación manejada por m2 (esencialmente igual que s4) puede ser manejada por m1, lo que significa que m2 sería más específico que m1, que es falso.

en un tipo parametrizado

Considera el siguiente código

 class PF { public void setValue(Parameter parameter, T value) { } public void setValue(Parameter parameter, Field value) { } } void test() PF pf2 = null; Parameter p2 = getP2(); Field f2 = getF2(); pf2.setValue(p2,f2); 

Esto comstack sin problema. Según 4.5.2, los tipos de los métodos en PF son métodos en PF con sustitución T=Object . Es decir, los métodos de pf2 son

  public void setValue(Parameter parameter, Object value) public void setValue(Parameter parameter, Field value) 

El segundo método es más específico que el primero.

Mi suposición es que el comstackdor está haciendo una resolución de sobrecarga de método según JLS, Sección 15.12.2.5 .

Para esta sección, el comstackdor utiliza una subtipificación fuerte (por lo tanto, no permite ninguna conversión no verificada), por lo tanto, el T value convierte en Object value y el Object value Field value convierte en el Field value Fieldvalue . Se aplicarán las siguientes reglas:

El método m es aplicable al subtipar si y solo si se cumplen las dos condiciones siguientes:

 * For 1in, either: o Ai is a subtype (§4.10) of Si (Ai <: Si) or o Ai is convertible to some type *Ci* by unchecked conversion 

(§5.1.9), y Ci <: Si. * Si m es un método genérico como se describe arriba, entonces Ul <: Bl [R1 = U1, ..., Rp = Up], 1lp.

(Consulte la viñeta 2). Dado que Field es un subtipo de Object se encuentra el método más específico. El campo f2 coincide con sus dos métodos (debido a la viñeta 2 anterior) y lo hace ambiguo.

Para String y Field , no hay una relación de subtipo entre los dos.

PD. Esta es mi comprensión de las cosas, no lo cito como kosher.

Editar : Esta respuesta es incorrecta. Eche un vistazo a la respuesta aceptada.

Creo que el problema se reduce a esto: el comstackdor no ve el tipo de f2 (es decir, Campo) y el tipo inferido de parámetro formal (es decir, Campo -> Campo) como el mismo tipo.

En otras palabras, parece que el tipo de f2 (Campo) se considera un subtipo del tipo de parámetro formal Campo (Campo). Como Field es del mismo tipo, un subtipo de Object, el comstackdor no puede elegir un método sobre otro.

Editar : Permítanme ampliar mi statement un poco

Ambos métodos son aplicables y parece que la Fase 1: Identificar los métodos de concordancia Aplicable mediante subtipificación se usa para decidir qué método llamar y cuáles reglas de Elegir el método más específico aplicado, pero falló por algún motivo para elegir el segundo método sobre el primero .

La sección de la Fase 1 usa esta notación: X <: S (X es el subtipo de S). Según mi comprensión de <: X <: X es una expresión válida, es decir, <: no es estricta e incluye el tipo en sí mismo (X es el subtipo de X) en este contexto. Esto explica el resultado de la Fase 1: ambos métodos se seleccionan como candidatos, ya que Field<: Object y Field<: Field .

Elegir la sección Método más específico utiliza la misma notación para decir que un método es más específico que otro. La parte interesante del párrafo que comienza con "Un método miembro de ariadia fija llamado m es más específico que otro miembro ...". Tiene, entre otras cosas:

Para todo j de 1 a n, Tj <: Sj.

Esto me hace pensar que, en nuestro caso, el segundo método debe elegirse sobre el primero, porque los siguientes son:

  • Parameter<: Parameter
  • Field<: Object
  • mientras que al revés no se cumple porque Object <: Field es falso (Object no es un subtipo de Field).

    Nota: En el caso de los ejemplos de Cadena, la Fase 1 simplemente elegirá el único método aplicable: el segundo.

    Entonces, para responder a sus preguntas: creo que esto es un error en la implementación del comstackdor. Eclipse tiene su propio comstackdor incremental que no tiene este error, parece.