Generics: compilatore sembra incapace di riconoscere che il tipo passato a un argomento è lo stesso di quello che viene restituito - perché?

StackOverflow https://stackoverflow.com/questions/3120428

  •  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>?

È stato utile?

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

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top