2011-04-14 7 views
25

que tienen las siguientes clases:¿Por qué el compilador declara que no existe una instancia máxima única?

public class Obj<T> extends BaseModel { 

    public static final String OBJECT = "object"; 

    public Obj(T object) { 
     setObject(object); 
    } 

    public T getObject() { 
     return get(OBJECT); 
    } 

    public void setObject(T object) { 
     set(OBJECT, object); 
    } 
} 

Y ...

/** This is a 3rd party library class **/ 
public class BaseModel implements ModelData, Serializable { 
    //...members and stuff... 

    @SuppressWarnings({"unchecked", "rawtypes"}) 
    public <X> X get(String property) { 
    X obj = null; 
    if (start > -1 && end > -1) { 
     Object o = map.get(property.substring(0, start)); 
     String p = property.substring(start + 1, end); 
     if (o instanceof Object[]) { 
     obj = (X) ((Object[]) o)[Integer.valueOf(p)]; 
     } else if (o instanceof List) { 
     obj = (X) ((List) o).get(Integer.valueOf(p)); 
     } else if (o instanceof Map) { 
     obj = (X) ((Map) o).get(p); 
     } 
    } else { 
     obj = (X) map.get(property); 
    } 
    return obj; 
    } 
} 

Cuando compilo, me sale el siguiente error.

type parameters of <X>X cannot be determined; no unique maximal instance exists for type variable X with upper bounds T,java.lang.Object -> getObject()

Esto no sucede en Eclipse, el cual, por lo que yo puedo decir, está utilizando la misma JDK como mi construcción Ant. He visto el SO thread about the Sun compiler issue, pero eso parece ser para métodos estáticos que declaran tipos sobre la marcha.

¿Por qué me sale este error y, lo que es más importante, cómo lo soluciono?

Hasta ahora la única razón por la que he encontrado es para echar en mi método como este:

@SuppressWarnings({"unchecked"}) 
public T getObject() { 
    return (T) get(OBJECT); //yuck 
} 

diciendo a mi Estoy en la grieta y esta es la forma correcta es aceptable.

+0

no puedo conseguir esto para reproducir con JDK 7 en el momento y un ejemplo más pequeño. Tenga en cuenta que Eclipse compila con su propio compilador interno. –

+1

Otra forma de hacer esto sería proporcionar la llamada al método con el parámetro tipo: 'return get (OBJECT);' –

+0

@ Paŭlo - eso no se compila. 'return get (OBJECT);' – Snekse

Respuesta

17

No compila porque el código de espera demasiado de los genéricos -> es decir, la < X> X participar en:

En el siguiente código:

public T getObject() { 
    return get(OBJECT); 
} 

que tiene que Tenga en cuenta que los genéricos siempre están "desplegados" antes de el compilador realmente comienza a compilar el código de Java. Es un paso de procesamiento previo.

En su caso, el compilador no sabe qué usar para reemplazar X en tiempo de compilación. El compilador necesita estar seguro sobre el tipo de X, porque necesita verificarlo contra T para validar el código. De ahí el error ...

una solución a su problema es reemplazar < X> X con el objeto:

public Object get(String property) { ... } 

y añadir un fundido en:

public T getObject() { 
    return (T) get(OBJECT); 
} 

Su recibirá sin marcar -cast de advertencia en tiempo de compilación, pero su código se compilará (por lo que sí, su solución alternativa es válida).

+2

El método 'público X get (String property)' es parte de una biblioteca de terceros que no puedo cambiar. – Snekse

+2

Ok, lo entiendo (esta biblioteca realmente no está escrita muy bien, lol). De todos modos, como usted mismo descubrió, un lanzamiento a (T) en getObject() solo resuelve el problema de compilación también. Desafortunadamente, no hay otra opción aquí ... – JVerstry

+0

De hecho, esta biblioteca parece un mal intento de implementar un contenedor heterogéneo ... – JVerstry

4

Los parámetros de tipo de método a menudo se deducen implícitamente de los argumentos de ese método. Nótese, sin embargo, get tiene ninguna relación explícita entre el argumento y el parámetro Tipo:

public <X> X get(String property) 

La inferencia de tipos es el camino habitual, pero los métodos también puede ser invocado con argumentos de tipo explícitas, al igual que las clases.El formato sigue más o menos el de la declaración, por lo que en el interior del Obj que podría tener

public T getObject() { 
    return super.<T>get(OBJECT); 
} 

También puedes, simplemente ser directo y utilizar <Object>, pero todavía se tendría que utilizar ese molde sin marcar para recuperarlo a T . Tenga en cuenta que el argumento explícito necesita un calificador, generalmente el nombre de instancia de la clase. Como su ejemplo usó un método de la superclase, su referencia está implícita a través del super.

Esto no resuelve el problema subyacente de aplicar un método genérico (<X> X get) dentro de una clase no genérica (BaseModel). Tenga en cuenta que el código en la biblioteca hace conversiones de tipo forzado al argumento de tipo. Este estilo es de hecho una de las soluciones para respaldar características genéricas en código Java no genérico. Parece que están tratando de ocultar esto de los usuarios de la biblioteca, pero como no genizaron la clase, no se puede deducir el tipo de la instancia (es decir, realmente desea tener Obj<T> extends BaseModel<T>).

[EDIT: corregido y se explica explícita argumento del tipo de método]

+0

El código 'return get (OBJECT);' no se compila. ¿Querías lanzar? – Snekse

+1

Vaya, olvidé dar la expresión calificativa. Lo arreglaré. Y no, realmente hay una diferencia entre un elenco y un argumento de tipo explícito. Aquí hay algo más [material explicativo] (http: // freddy33.blogspot.com/2007/11/ugly-duckling-of-java.html). –

1

acabo encontró con un problema similar con un proyecto utilizando Apache Pivot. El código de cliente estaba plagado de líneas como:

boolean foo = org.apache.pivot.json.JSON.get(item, "foo"); 

el código compile en Eclipse, pero no usando Maven o javac desde la línea de comandos. Parece ser Bug 6302954, pero aún lo veo después de actualizar a la última JDK.

medida que la clase JSON es proporcionada por pivote, no es algo que podría modificar dentro de mi propio árbol de código fuente (bifurcar la biblioteca no es una opción en este proyecto)

La solución que funcionó para mí vino de la primera responder en el informe de error, cambiar el código para leer:

boolean foo = org.apache.pivot.json.JSON.<Boolean>get(item, "foo"); 
+0

Esto funciona bien si conoce el tipo de objeto con anticipación, pero en el caso donde el tipo se determina en tiempo de ejecución como parte de la creación de la instancia de clase ('new Obj (new Foo())', esto para doesn ' t trabajo. – Snekse

21

ésta es ficticia bug que se ha fijado en Java sE 7.

+0

¡Gracias por el enlace al defecto! – asgs

+1

Curiosamente, solo me encontraba con OpenJDK 6 en Ubuntu, no con Java 1.6 en Windows. Al cambiar a OpenJDK 7 el problema desapareció en mi máquina Ubuntu. –

+1

El uso de jdk 1.7 para compilar el código solucionó este problema – Sahil

Cuestiones relacionadas