Generics: compilatore sembra incapace di riconoscere che il tipo passato a un argomento è lo stesso di quello che viene restituito - perché?
-
30-09-2019 - |
Domanda
Diciamo che ho parecchie POJO cui tutti si estendono un supertipo comune BaseObject
.
Ho un GenericDao
che viene dichiarato come public interface GenericDao<T>
.
Per ciascun tipo-specifico DAO, ho un interfaccia che estende il tipo generico e la limita a un tipo concreto (public interface UserDao extends GenericDao<User>
) e quindi un'implementazione del DAO tipo-specifica.
In una classe che tenta di utilizzare una serie di implementazioni GenericDao
, ho un metodo che appare come
public <T extends BaseObject> long create(T object) {
return getDao(object.getClass()).save(object);
}
Se implemento getDao()
in modo che sia il parametro è un oggetto Class
, come ad esempio
private <T extends BaseObject> GenericDao<T> getDao(Class<T> clazz) { ... }
Poi la chiamata a getDao(object.getClass()
nel metodo create()
fallisce la compilazione - il compilatore sembra interpretare il tipo di ritorno di getDao()
come
GenericDao<? extends BaseContractObject>
piuttosto che riconoscere che getDao(Class<T>)
sta per tornare me un GenericDao
dello stesso tipo T
.
qualcuno può spiegare perché questo è? Ho capito che ripetuti apparenze dello stesso tipo o vincolato jolly non consentono necessario fare riferimento allo stesso tipo; tuttavia sembra che il compilatore dovrebbe riconoscere dalla firma del getDao(Class<T>)
che il T passava dovrebbe essere lo stesso T restituito (ma ovviamente non è in grado di riconoscere questo, il perché è la parte fallisco per cogliere).
Se io invece definisco la firma di getDao
essere
private <T extends BaseContractObject> GenericDao<T> getDao(T obj) { ... }
Quindi non v'è alcun problema nella compilazione di un'implementazione create()
che assomiglia
public <T extends BaseContractObject> long create(T object) {
return getDao(object).save(object);
}
Allora, perché è il compilatore in grado di riconoscere in questo caso che l'argomento passato alla T
getDao(T)
è lo stesso T
nel tipo di ritorno, mentre non poteva riconoscere questo quando l'argomento era Class<T>
?
Soluzione
Il object.getClass()
espressione, in cui oggetto è di tipo T extends BaseObject
, restituisce un Class<? extends BaseObject>
, non un Class<T>
come ci si potrebbe aspettare. Quindi, getDao()
restituisce un DAO dello stesso tipo che riceve; non è solo ricevere il tipo previsto.
Altri suggerimenti
Questo è un problema classico tipo di cancellazione. getClass()
ha la seguente firma:
public final native Class<? extends Object> getClass();
Se si dispone di un String
e fare una getClass()
su di esso, la classe si ottiene è Class<? extends String>
. I javadocs leggere:
* @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.
Sarà necessario forzare il seguente cast farlo funzionare:
@SuppressWarnings("unchecked")
Class<T> clazz = (Class<T>)object.getClass();
return getDao(clazz).save(object);
che funziona per me.
Credo che questo dovrebbe spiegare perché il vincolo non sta facendo quello che ci si aspetta:
http://www.angelikalanger.com/GenericsFAQ/FAQSections/TypeParameters .html # FAQ206