Genéricos: Compilador parece incapaz de reconocer que el tipo pasado de un argumento es el mismo que lo que se devuelve - ¿por qué?
-
30-09-2019 - |
Pregunta
Digamos que tengo varios POJOs la que todos se extienden un supertipo común, BaseObject
.
Tengo un GenericDao
que se declara como public interface GenericDao<T>
.
Para cada DAO específica del tipo, tengo una interfaz que se extiende el tipo genérico y la restringe a un tipo concreto (public interface UserDao extends GenericDao<User>
) y luego una implementación de la DAO-tipo específico.
En una clase que los intentos de utilizar una serie de implementaciones GenericDao
, tengo un método que se parece a
public <T extends BaseObject> long create(T object) {
return getDao(object.getClass()).save(object);
}
Si implemento getDao()
para que de parámetro es un objeto Class
, como
private <T extends BaseObject> GenericDao<T> getDao(Class<T> clazz) { ... }
A continuación, la llamada a getDao(object.getClass()
en el método create()
falla al compilar - el compilador parece interpretar el tipo de retorno de getDao()
como
GenericDao<? extends BaseContractObject>
en lugar de reconocer que getDao(Class<T>)
me va a devolver un GenericDao
del mismo tipo T
.
Puede alguien explicar por qué esto es? Entiendo que repetidas apariciones del mismo tipo unido o Comodín No es necesario hacer referencia al mismo tipo; Sin embargo parece que el compilador debe reconocer a partir de la firma de getDao(Class<T>)
que el T aprobada en la misma debe ser devuelto T (pero obviamente no es capaz de reconocer esto, el ¿Por qué es la parte fallo para comprender).
Si en cambio defino la firma de getDao
ser
private <T extends BaseContractObject> GenericDao<T> getDao(T obj) { ... }
A continuación, no hay ningún problema en la compilación de una aplicación create()
que se parece a
public <T extends BaseContractObject> long create(T object) {
return getDao(object).save(object);
}
¿Por qué es el compilador capaz de reconocer en este caso que el argumento T
pasado a getDao(T)
es el mismo T
en el tipo de retorno, mientras que no podía reconocer esto cuando el argumento era Class<T>
?
Solución
El object.getClass()
expresión, donde objeto es de tipo T extends BaseObject
, devuelve un Class<? extends BaseObject>
, no un Class<T>
como uno podría esperar. Así, getDao()
devuelve un DAO del mismo tipo que recibe; sólo que no está recibiendo el tipo esperado.
Otros consejos
Este es un tema clásico de tipo borrado. getClass()
tiene la firma siguiente:
public final native Class<? extends Object> getClass();
Si usted tiene un String
y hacer un getClass()
en él, la clase que se obtiene es Class<? extends String>
. Los javadocs leen:
* @return The <code>java.lang.Class</code> object that represents
* the runtime class of the object. The result is of type
* {@code Class<? extends X>} where X is the
* erasure of the static type of the expression on which
* <code>getClass</code> is called.
tendrá que forzar la siguiente conversión al conseguir que funcione:
@SuppressWarnings("unchecked")
Class<T> clazz = (Class<T>)object.getClass();
return getDao(clazz).save(object);
que funciona para mí.
Creo que esto debería explicar por qué la restricción no está haciendo lo que se espera:
http://www.angelikalanger.com/GenericsFAQ/FAQSections/TypeParameters .html # FAQ206