Comment puis-je atteindre cet objectif?
public class GenericClass<T>
{
public Type getMyType()
{
//How do I return the type of T?
}
}
Tout ce que j'ai essayé jusqu'à présent renvoie toujours le type Object
plutôt que le type spécifique utilisé.
Comme d'autres l'ont mentionné, ce n'est possible que par réflexion dans certaines circonstances.
Si vous avez vraiment besoin du type, il s'agit du modèle de solution de contournement habituel (adapté au type):
public class GenericClass<T> {
private final Class<T> type;
public GenericClass(Class<T> type) {
this.type = type;
}
public Class<T> getMyType() {
return this.type;
}
}
J'ai vu quelque chose comme ça
private Class<T> persistentClass;
public Constructor() {
this.persistentClass = (Class<T>) ((ParameterizedType) getClass()
.getGenericSuperclass()).getActualTypeArguments()[0];
}
dans le hibernate GenericDataAccessObjects Example
Les génériques ne sont pas reifiés au moment de l'exécution. Cela signifie que les informations ne sont pas présentes au moment de l'exécution.
Ajouter des génériques à Java tout en maintenant la compatibilité avec les versions antérieures était un tour de force (vous pouvez voir le document fondamental à son sujet: Assurer l'avenir du passé: ajouter de la généricité au langage de programmation Java ).
Il existe une littérature abondante sur le sujet, et certaines personnes sont insatisfaites avec l'état actuel, d'autres disent qu'en réalité c'est un { leurre } _ et qu'il n'est pas réellement nécessaire. Vous pouvez lire les deux liens, je les ai trouvés assez intéressants.
Utilisez la goyave.
import com.google.common.reflect.TypeToken;
import Java.lang.reflect.Type;
public abstract class GenericClass<T> {
private final TypeToken<T> typeToken = new TypeToken<T>(getClass()) { };
private final Type type = typeToken.getType(); // or getRawType() to return Class<? super T>
public Type getType() {
return type;
}
public static void main(String[] args) {
GenericClass<String> example = new GenericClass<String>() { };
System.out.println(example.getType()); // => class Java.lang.String
}
}
Il y a quelque temps, j'ai posté des exemples complets, y compris des classes abstraites et des sous-classes ici .
Remarque: pour cela, vous devez instancier une sous-classe sur GenericClass
pour pouvoir associer correctement le paramètre type. Sinon, le type est simplement retourné sous la forme T
.
Sûr que vous pouvez.
Java n'utilise pas l'information au moment de l'exécution, pour des raisons de compatibilité avec les versions antérieures. Mais les informations sont réellement présentes sous forme de métadonnées et sont accessibles par réflexion (mais elles ne sont toujours pas utilisées pour la vérification de type).
De l'API officielle:
Cependant, pour votre scénario, je n’utiliserais pas la réflexion. Personnellement, je suis plus enclin à utiliser cela pour le code cadre. Dans votre cas, je voudrais simplement ajouter le type en tant que paramètre constructeur.
Les génériques Java sont principalement lors de la compilation, cela signifie que les informations de type sont perdues lors de l'exécution.
class GenericCls<T>
{
T t;
}
sera compilé à quelque chose comme
class GenericCls
{
Object o;
}
Pour obtenir les informations de type au moment de l'exécution, vous devez les ajouter en tant qu'argument du ctor.
class GenericCls<T>
{
private Class<T> type;
public GenericCls(Class<T> cls)
{
type= cls;
}
Class<T> getType(){return type;}
}
Exemple:
GenericCls<?> instance = new GenericCls<String>(String.class);
assert instance.getType() == String.class;
J'ai utilisé l'approche suivante:
public class A<T> {
protected Class<T> clazz;
public A() {
this.clazz = (Class<T>) ((ParameterizedType) getClass().getGenericSuperclass()).getActualTypeArguments()[0];
}
public Class<T> getClazz() {
return clazz;
}
}
public class B extends A<C> {
/* ... */
public void anything() {
// here I may use getClazz();
}
}
La technique décrite dans ce article de Ian Robertson fonctionne pour moi.
En bref exemple rapide et sale:
public abstract class AbstractDAO<T extends EntityInterface, U extends QueryCriteria, V>
{
/**
* Method returns class implementing EntityInterface which was used in class
* extending AbstractDAO
*
* @return Class<T extends EntityInterface>
*/
public Class<T> returnedClass()
{
return (Class<T>) getTypeArguments(AbstractDAO.class, getClass()).get(0);
}
/**
* Get the underlying class for a type, or null if the type is a variable
* type.
*
* @param type the type
* @return the underlying class
*/
public static Class<?> getClass(Type type)
{
if (type instanceof Class) {
return (Class) type;
} else if (type instanceof ParameterizedType) {
return getClass(((ParameterizedType) type).getRawType());
} else if (type instanceof GenericArrayType) {
Type componentType = ((GenericArrayType) type).getGenericComponentType();
Class<?> componentClass = getClass(componentType);
if (componentClass != null) {
return Array.newInstance(componentClass, 0).getClass();
} else {
return null;
}
} else {
return null;
}
}
/**
* Get the actual type arguments a child class has used to extend a generic
* base class.
*
* @param baseClass the base class
* @param childClass the child class
* @return a list of the raw classes for the actual type arguments.
*/
public static <T> List<Class<?>> getTypeArguments(
Class<T> baseClass, Class<? extends T> childClass)
{
Map<Type, Type> resolvedTypes = new HashMap<Type, Type>();
Type type = childClass;
// start walking up the inheritance hierarchy until we hit baseClass
while (!getClass(type).equals(baseClass)) {
if (type instanceof Class) {
// there is no useful information for us in raw types, so just keep going.
type = ((Class) type).getGenericSuperclass();
} else {
ParameterizedType parameterizedType = (ParameterizedType) type;
Class<?> rawType = (Class) parameterizedType.getRawType();
Type[] actualTypeArguments = parameterizedType.getActualTypeArguments();
TypeVariable<?>[] typeParameters = rawType.getTypeParameters();
for (int i = 0; i < actualTypeArguments.length; i++) {
resolvedTypes.put(typeParameters[i], actualTypeArguments[i]);
}
if (!rawType.equals(baseClass)) {
type = rawType.getGenericSuperclass();
}
}
}
// finally, for each actual type argument provided to baseClass, determine (if possible)
// the raw class for that type argument.
Type[] actualTypeArguments;
if (type instanceof Class) {
actualTypeArguments = ((Class) type).getTypeParameters();
} else {
actualTypeArguments = ((ParameterizedType) type).getActualTypeArguments();
}
List<Class<?>> typeArgumentsAsClasses = new ArrayList<Class<?>>();
// resolve types by chasing down type variables.
for (Type baseType : actualTypeArguments) {
while (resolvedTypes.containsKey(baseType)) {
baseType = resolvedTypes.get(baseType);
}
typeArgumentsAsClasses.add(getClass(baseType));
}
return typeArgumentsAsClasses;
}
}
Je ne pense pas que vous puissiez le faire, Java utilise la suppression de type lors de la compilation afin que votre code soit compatible avec les applications et les bibliothèques créées avant les génériques.
À partir des documents Oracle:
Type d'effacement
Les génériques ont été introduits dans le langage Java pour fournir un type plus étroit vérifie au moment de la compilation et prend en charge la programmation générique. À implémenter des génériques, le compilateur Java applique un effacement de type à:
Remplacez tous les paramètres de type des types génériques par leurs bornes ou Object si les paramètres de type ne sont pas liés. Le bytecode produit, par conséquent, ne contient que des classes, interfaces et méthodes ordinaires . Insérer des caractères si nécessaire pour préserver la sécurité du type. Produire Méthodes de pontage pour préserver le polymorphisme dans les types génériques étendus . La suppression de type garantit qu'aucune nouvelle classe n'est créée pour paramétré les types; par conséquent, les génériques n'engendrent aucune surcharge d'exécution.
http://docs.Oracle.com/javase/tutorial/Java/generics/erasure.html
public abstract class AbstractDao<T>
{
private final Class<T> persistentClass;
public AbstractDao()
{
this.persistentClass = (Class<T>) ((ParameterizedType) this.getClass().getGenericSuperclass())
.getActualTypeArguments()[0];
}
}
Ceci est ma solution:
import Java.lang.reflect.Type;
import Java.lang.reflect.TypeVariable;
public class GenericClass<T extends String> {
public static void main(String[] args) {
for (TypeVariable typeParam : GenericClass.class.getTypeParameters()) {
System.out.println(typeParam.getName());
for (Type bound : typeParam.getBounds()) {
System.out.println(bound);
}
}
}
}
Je pense qu'il y a une autre solution élégante.
Ce que vous voulez faire est de "passer" (en toute sécurité) le type du paramètre de type générique de la classe concerete à la superclasse.
Si vous vous permettez de considérer le type de classe comme "métadonnées" sur la classe, cela suggère la méthode Java pour coder les métadonnées au moment de l'exécution: les annotations.
Commencez par définir une annotation personnalisée selon ces lignes:
import Java.lang.annotation.*;
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface EntityAnnotation {
Class entityClass();
}
Vous pouvez ensuite avoir à ajouter l'annotation à votre sous-classe.
@EntityAnnotation(entityClass = PassedGenericType.class)
public class Subclass<PassedGenericType> {...}
Ensuite, vous pouvez utiliser ce code pour obtenir le type de classe dans votre classe de base:
import org.springframework.core.annotation.AnnotationUtils;
.
.
.
private Class getGenericParameterType() {
final Class aClass = this.getClass();
EntityAnnotation ne =
AnnotationUtils.findAnnotation(aClass, EntityAnnotation.class);
return ne.entityClass();
}
Certaines limites de cette approche sont:
PassedGenericType
) dans deux emplacements plutôt que dans un emplacement non-DRY.Voici la solution de travail !!!
@SuppressWarnings("unchecked")
private Class<T> getGenericTypeClass() {
try {
String className = ((ParameterizedType) getClass().getGenericSuperclass()).getActualTypeArguments()[0].getTypeName();
Class<?> clazz = Class.forName(className);
return (Class<T>) clazz;
} catch (Exception e) {
throw new IllegalStateException("Class is not parametrized with generic type!!! Please use extends <> ");
}
}
NOTES: Peut être utilisé uniquement comme superclasse
1. Doit être étendu avec une classe typée (Child extends Generic<Integer>
)
OU
2. Doit être créé en tant qu'implémentation anonyme (new Generic<Integer>() {};
)
Une solution simple pour cela peut être comme ci-dessous
public class GenericDemo<T>{
private T type;
GenericDemo(T t)
{
this.type = t;
}
public String getType()
{
return this.type.getClass().getName();
}
public static void main(String[] args)
{
GenericDemo<Integer> obj = new GenericDemo<Integer>(5);
System.out.println("Type: "+ obj.getType());
}
}
Voici un moyen que j'ai dû utiliser une ou deux fois:
public abstract class GenericClass<T>{
public abstract Class<T> getMyType();
}
De même que
public class SpecificClass extends GenericClass<String>{
@Override
public Class<String> getMyType(){
return String.class;
}
}
Tu ne peux pas. Si vous ajoutez une variable membre de type T à la classe (vous n'avez même pas besoin de l'initialiser), vous pouvez l'utiliser pour récupérer le type.
Juste au cas où vous utiliseriez stocker une variable en utilisant le type générique, vous pouvez facilement résoudre ce problème en ajoutant une méthode getClassType comme suit:
public class Constant<T> {
private T value;
@SuppressWarnings("unchecked")
public Class<T> getClassType () {
return ((Class<T>) value.getClass());
}
}
J'utilise plus tard l'objet de classe fourni pour vérifier s'il s'agit d'une instance d'une classe donnée, comme suit:
Constant<?> constant = ...;
if (constant.getClassType().equals(Integer.class)) {
Constant<Integer> integerConstant = (Constant<Integer>)constant;
Integer value = integerConstant.getValue();
// ...
}
Pour compléter certaines des réponses ici, j'ai dû obtenir le ParametrizedType de MyGenericClass, quelle que soit la hiérarchie, à l'aide de la récursivité:
private Class<T> getGenericTypeClass() {
return (Class<T>) (getParametrizedType(getClass())).getActualTypeArguments()[0];
}
private static ParameterizedType getParametrizedType(Class clazz){
if(clazz.getSuperclass().equals(MyGenericClass.class)){ // check that we are at the top of the hierarchy
return (ParameterizedType) clazz.getGenericSuperclass();
} else {
return getParametrizedType(clazz.getSuperclass());
}
}
Voici ma solution
public class GenericClass<T>
{
private Class<T> realType;
public GenericClass() {
findTypeArguments(getClass());
}
private void findTypeArguments(Type t) {
if (t instanceof ParameterizedType) {
Type[] typeArgs = ((ParameterizedType) t).getActualTypeArguments();
realType = (Class<T>) typeArgs[0];
} else {
Class c = (Class) t;
findTypeArguments(c.getGenericSuperclass());
}
}
public Type getMyType()
{
// How do I return the type of T? (your question)
return realType;
}
}
Quel que soit le niveau de votre hiérarchie de classe, Cette solution fonctionne toujours, par exemple:
public class FirstLevelChild<T> extends GenericClass<T> {
}
public class SecondLevelChild extends FirstLevelChild<String> {
}
Dans ce cas, getMyType () = Java.lang.String
Voici mon astuce:
public class Main {
public static void main(String[] args) throws Exception {
System.out.println(Main.<String> getClazz());
}
static <T> Class getClazz(T... param) {
return param.getClass().getComponentType();
}
}
public static final Class<?> getGenericArgument(final Class<?> clazz)
{
return (Class<?>) ((ParameterizedType) clazz.getGenericSuperclass()).getActualTypeArguments()[0];
}