Génériques: compilateur semble incapable de reconnaître que le type est passé à un argument est le même que ce qui est retourné - pourquoi?
-
30-09-2019 - |
Question
Disons que j'ai plusieurs POJO qui se prolongent tout une commune supertype, BaseObject
.
J'ai un GenericDao
qui est déclarée comme public interface GenericDao<T>
.
Pour chaque DAO spécifique de type, j'ai une interface qui étend le type générique et le limite à un type de béton (public interface UserDao extends GenericDao<User>
), puis une mise en oeuvre du DAO type spécifique.
Dans une classe qui tente d'utiliser un certain nombre d'implémentations de GenericDao
, j'ai une méthode qui ressemble à
public <T extends BaseObject> long create(T object) {
return getDao(object.getClass()).save(object);
}
Si je mets en œuvre getDao()
de telle sorte que le paramètre de c'est un objet Class
, comme
private <T extends BaseObject> GenericDao<T> getDao(Class<T> clazz) { ... }
Alors l'appel à getDao(object.getClass()
dans la méthode create()
ne peut pas compiler - le compilateur semble interpréter le type de retour de getDao()
comme
GenericDao<? extends BaseContractObject>
au lieu de reconnaître que getDao(Class<T>)
va me retourner un GenericDao
du même type T
.
Quelqu'un peut-il expliquer pourquoi il en est? Je comprends que les apparences répétées du même type lié ou caractère générique ne correspondent pas nécessairement se référer au même type; mais il semble que le compilateur doit reconnaître à partir de la signature de getDao(Class<T>)
que le T adoptée en devrait être le même T retourné (mais évidemment il n'est pas capable de reconnaître cela, le pourquoi est la partie je ne à saisir).
Si je définis au lieu de la signature getDao
être
private <T extends BaseContractObject> GenericDao<T> getDao(T obj) { ... }
Alors il n'y a pas de problème dans la compilation d'une mise en œuvre de create()
qui ressemble à
public <T extends BaseContractObject> long create(T object) {
return getDao(object).save(object);
}
Alors, pourquoi le compilateur capable de reconnaître dans ce cas que l'argument de T
passé à getDao(T)
est le même T
dans le type de retour, alors qu'il ne pouvait pas reconnaître quand l'argument était Class<T>
?
La solution
Le object.getClass()
d'expression, où l'objet est de type T extends BaseObject
, retourne un Class<? extends BaseObject>
, pas Class<T>
comme on pouvait s'y attendre. Ainsi, getDao()
renvoie une OAC du même type qu'il reçoit; il est tout simplement pas recevoir le type attendu.
Autres conseils
Ceci est un problème d'effacement de type classique. getClass()
a la signature suivante:
public final native Class<? extends Object> getClass();
Si vous avez un String
et faire une getClass()
là-dessus, la classe que vous obtenez est Class<? extends String>
. Les javadocs lire:
* @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.
Vous aurez besoin de forcer la distribution suivante pour l'obtenir au travail:
@SuppressWarnings("unchecked")
Class<T> clazz = (Class<T>)object.getClass();
return getDao(clazz).save(object);
qui fonctionne pour moi.
Je pense que cela devrait expliquer pourquoi la contrainte ne fait pas ce que vous attendez:
http://www.angelikalanger.com/GenericsFAQ/FAQSections/TypeParameters .html # FAQ206