Je crée une classe générique et dans l'une des méthodes j'ai besoin de connaître la classe du type générique actuellement utilisé. La raison en est que l'un des appels I de la méthode attend cela comme argument.
Exemple:
public class MyGenericClass<T> {
public void doSomething() {
// Snip...
// Call to a 3rd party lib
T bean = (T)someObject.create(T.class);
// Snip...
}
}
De toute évidence, l'exemple ci-dessus ne fonctionne pas et entraîne l'erreur suivante: Littéral de classe non autorisé pour le paramètre de type T.
Ma question est: quelqu'un connaît-il une bonne alternative ou solution de contournement pour cela?
Toujours les mêmes problèmes: les informations génériques sont effacées lors de l'exécution, elles ne peuvent pas être récupérées. Une solution consiste à passer la classe T en paramètre d'une méthode statique:
public class MyGenericClass<T> {
private final Class<T> clazz;
public static <U> MyGenericClass<U> createMyGeneric(Class<U> clazz) {
return new MyGenericClass<U>(clazz);
}
protected MyGenericClass(Class<T> clazz) {
this.clazz = clazz;
}
public void doSomething() {
T instance = clazz.newInstance();
}
}
C'est moche, mais ça marche.
Je viens de signaler cette solution:
import Java.lang.reflect.ParameterizedType;
public abstract class A<B> {
public Class<B> g() throws Exception {
ParameterizedType superclass =
(ParameterizedType) getClass().getGenericSuperclass();
return (Class<B>) superclass.getActualTypeArguments()[0];
}
}
Cela fonctionne si A
se voit attribuer un type concret par une sous-classe:
new A<String>() {}.g() // this will work
class B extends A<String> {}
new B().g() // this will work
class C<T> extends A<T> {}
new C<String>().g() // this will NOT work
Malheureusement, la solution de Christoph telle qu'elle est écrite ne fonctionne que dans des circonstances très limitées. [EDIT: comme indiqué ci-dessous, je ne me souviens plus de mon raisonnement pour cette phrase et il est probablement faux: "Notez que cela ne fonctionnera que dans les classes abstraites, tout d'abord."] La prochaine difficulté est que g()
ne fonctionne qu'à partir des sous-classes DIRECT de A
. Nous pouvons résoudre ce problème, cependant:
private Class<?> extractClassFromType(Type t) throws ClassCastException {
if (t instanceof Class<?>) {
return (Class<?>)t;
}
return (Class<?>)((ParameterizedType)t).getRawType();
}
public Class<B> g() throws ClassCastException {
Class<?> superClass = getClass(); // initial value
Type superType;
do {
superType = superClass.getGenericSuperclass();
superClass = extractClassFromType(superType);
} while (! (superClass.equals(A.class)));
Type actualArg = ((ParameterizedType)superType).getActualTypeArguments()[0];
return (Class<B>)extractClassFromType(actualArg);
}
Cela fonctionnera dans de nombreuses situations dans la pratique, mais pas tout le temps. Considérer:
public class Foo<U,T extends Collection<?>> extends A<T> {}
(new Foo<String,List<Object>>() {}).g();
Cela lancera un ClassCastException
, car l'argument type ici n'est pas du tout un Class
ou un ParameterizedType
; c'est le TypeVariable
T
. Alors maintenant, vous seriez coincé à essayer de comprendre quel type T
était censé représenter, et ainsi de suite dans le terrier du lapin.
Je pense que la seule réponse raisonnable et générale s'apparente à la réponse initiale de Nicolas - en général, si votre classe a besoin d'instancier des objets d'une autre classe inconnue au moment de la compilation, les utilisateurs de votre classe doivent passer cette classe littéralement ( ou, peut-être, une usine) à votre classe de manière explicite et ne pas compter uniquement sur des génériques.
je trouve un autre moyen d'obtenir la classe de l'objet générique
public Class<?> getGenericClass(){
Class<?> result =null;
Type type =this.getClass().getGenericSuperclass();
if(type instanceofParameterizedType){
ParameterizedType pt =(ParameterizedType) type;
Type[] fieldArgTypes = pt.getActualTypeArguments();
result =(Class<?>) fieldArgTypes[0];
}
return result;
}
T peut être résolu assez facilement en utilisant TypeTools :
Class<T> t = (Class<T>) TypeResolver.resolveRawArguments(
MyGenericClass.class, getClass());
Je développerai la solution de Christoph.
Voici la classe abstraite ClassGetter:
private abstract class ClassGetter<T> {
public final Class<T> get() {
final ParameterizedType superclass = (ParameterizedType)
getClass().getGenericSuperclass();
return (Class<T>)superclass.getActualTypeArguments()[0];
}
}
Voici une méthode statique qui utilise la classe ci-dessus pour trouver un type de classe générique:
public static <T> Class<T> getGenericClass() {
return new ClassGetter<T>() {}.get();
}
À titre d'exemple de son utilisation, vous pouvez créer cette méthode:
public static final <T> T instantiate() {
final Class<T> clazz = getGenericClass();
try {
return clazz.getConstructor((Class[])null).newInstance(null);
} catch (Exception e) {
return null;
}
}
Et puis utilisez-le comme ceci:
T var = instantiate();