it-swarm.com.de

Generischen Klassentyp zur Laufzeit abrufen

Wie kann ich das erreichen?

public class GenericClass<T>
{
    public Type getMyType()
    {
        //How do I return the type of T?
    }
}

Alles, was ich bisher ausprobiert habe, gibt immer den Typ Object und nicht den spezifischen Typ zurück.

411
Glenn

Wie bereits erwähnt, ist dies nur unter bestimmten Umständen durch Reflexion möglich.

Wenn Sie den Typ wirklich benötigen, ist dies das übliche (typsichere) Workaround-Muster:

public class GenericClass<T> {

     private final Class<T> type;

     public GenericClass(Class<T> type) {
          this.type = type;
     }

     public Class<T> getMyType() {
         return this.type;
     }
}
271
Henning

Ich habe so etwas gesehen

private Class<T> persistentClass;

public Constructor() {
    this.persistentClass = (Class<T>) ((ParameterizedType) getClass()
                            .getGenericSuperclass()).getActualTypeArguments()[0];
 }

im hibernate-Beispiel für GenericDataAccessObjects

221
FrVaBe

Generics sind zur Laufzeit nicht reified. Dies bedeutet, dass die Informationen zur Laufzeit nicht vorhanden sind.

Das Hinzufügen von Generics zu Java bei gleichzeitiger Beibehaltung der Rückwärtskompatibilität war eine Tour-de-Force (Sie können den wichtigen Beitrag dazu lesen: Zukunftssicherung für die Vergangenheit: Die Java-Programmiersprache mit Generizität versehen ).

Es gibt eine reiche Literatur zu diesem Thema, und einige Leute sind unzufrieden mit dem gegenwärtigen Stand, einige sagen, dass es tatsächlich ein Lockmittel ist und es besteht keine wirkliche Notwendigkeit dafür. Sie können beide Links lesen, ich fand sie sehr interessant.

83
ewernli

Verwenden Sie Guave.

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
  }
}

Vor einiger Zeit habe ich einige ausführliche Beispiele veröffentlicht, einschließlich abstrakter Klassen und Unterklassen hier .

Hinweis: Dies erfordert, dass Sie eine subclass von GenericClass instanziieren, damit der type-Parameter korrekt gebunden werden kann. Andernfalls wird der Typ einfach als T zurückgegeben.

50
Cody A. Ray

Sicher kannst du.

Java verwendet die Informationen zur Laufzeit aus Gründen der Abwärtskompatibilität nicht . Die Informationen sind jedoch tatsächlich als Metadaten vorhanden und können über Reflektion abgerufen werden (sie werden jedoch immer noch nicht für die Typprüfung verwendet).

Von der offiziellen API:

http://download.Oracle.com/javase/6/docs/api/Java/lang/reflect/ParameterizedType.html#getActualTypeArguments%28%29

Allerdings würde ich für Ihr Szenario keine Reflexion verwenden. Ich persönlich bin eher geneigt, das für Framework-Code zu verwenden. In Ihrem Fall würde ich den Typ einfach als Konstruktorparameter hinzufügen.

37
Joeri Hendrickx

Java-Generics haben meistens Kompilierungszeit, dh die Typinformationen gehen zur Laufzeit verloren.

class GenericCls<T>
{
    T t;
}

wird so etwas kompiliert

class GenericCls
{
   Object o;
}

Um die Typinformationen zur Laufzeit abzurufen, müssen Sie sie als Argument des ctor hinzufügen.

class GenericCls<T>
{
     private Class<T> type;
     public GenericCls(Class<T> cls)
     {
        type= cls;
     }
     Class<T> getType(){return type;}
}

Beispiel:

GenericCls<?> instance = new GenericCls<String>(String.class);
assert instance.getType() == String.class;
33
josefx

Ich habe folgenden Ansatz verwendet:

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();
    }
}
21
Moesio

Die in diesem Artikel von Ian Robertson beschriebene Technik arbeitet für mich.

Kurzes und schmutziges Beispiel:

 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;
    }
  }
14
Ondrej Bozek

Ich glaube nicht, dass Sie dies tun können. Java verwendet beim Kompilieren die Typenlöschung, sodass Ihr Code mit Anwendungen und Bibliotheken kompatibel ist, die vorgeneriert wurden.

Aus den Oracle-Dokumenten:

Typ löschen

Generics wurden in die Java-Sprache eingeführt, um einen engeren Typ zu bieten prüft zur Kompilierzeit und unterstützt die generische Programmierung. Zu Generics implementieren, wendet der Java-Compiler die Typlöschung an:

Ersetzen Sie alle Typparameter in generischen Typen durch ihre Grenzen oder Objekt, wenn die Typparameter unbegrenzt sind. Der erzeugte Bytecode enthält daher nur gewöhnliche Klassen, Schnittstellen und Methoden . Fügen Sie ggf. Gipsmodelle ein, um die Typsicherheit zu gewährleisten. Generieren Überbrückungsmethoden zur Erhaltung des Polymorphismus in erweiterten generischen Typen . Durch die Typlöschung wird sichergestellt, dass keine neuen Klassen für parametrisierte .__ erstellt werden. Arten; Generika haben daher keinen Laufzeitaufwand.

http://docs.Oracle.com/javase/tutorial/Java/generics/erasure.html

12
Dimitar
public abstract class AbstractDao<T>
{
    private final Class<T> persistentClass;

    public AbstractDao()
    {
        this.persistentClass = (Class<T>) ((ParameterizedType) this.getClass().getGenericSuperclass())
                .getActualTypeArguments()[0];
    }
}
9
Ali Yeganeh

Das ist meine Lösung:

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);
      }
    }
  }
}
8
Matthias M

Ich denke, dass es eine andere elegante Lösung gibt.

Was Sie tun möchten, ist (sicher) den Typ des generischen Typparameters von der concerete-Klasse an die Oberklasse zu übergeben.

Wenn Sie sich den Klassentyp als "Metadaten" für die Klasse vorstellen, schlägt dies die Java-Methode zum Codieren von Metadaten zur Laufzeit vor: Annotationen.

Definieren Sie zunächst eine benutzerdefinierte Anmerkung entlang der folgenden Zeilen:

import Java.lang.annotation.*;

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface EntityAnnotation {
    Class entityClass();
}

Sie können die Anmerkung dann Ihrer Unterklasse hinzufügen.

@EntityAnnotation(entityClass =  PassedGenericType.class)
public class Subclass<PassedGenericType> {...}

Dann können Sie diesen Code verwenden, um den Klassentyp in Ihrer Basisklasse abzurufen:

import org.springframework.core.annotation.AnnotationUtils;
.
.
.

private Class getGenericParameterType() {
    final Class aClass = this.getClass();
    EntityAnnotation ne = 
         AnnotationUtils.findAnnotation(aClass, EntityAnnotation.class);

    return ne.entityClass();
}

Einige Einschränkungen dieses Ansatzes sind:

  1. Sie geben den generischen Typ (PassedGenericType) in ZWEI Orten an und nicht in einer, die nicht DRY ist.
  2. Dies ist nur möglich, wenn Sie die konkreten Unterklassen ändern können.
8
DaBlick

Hier funktioniert die Lösung !!!

@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 <> ");
        }
    } 

HINWEISE: Kann nur als Oberklasse .__ verwendet werden. 
1. Muss mit typisierter Klasse (Child extends Generic<Integer>) erweitert werden
ODER
 
2. Muss als anonyme Implementierung erstellt werden (new Generic<Integer>() {};

5
Yaroslav Kovbas

Eine einfache Lösung für dieses Fahrerhaus ist wie folgt

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());
    }
}
3
Paul92

Hier ist eine Möglichkeit, die ich ein- oder zweimal verwenden musste:

public abstract class GenericClass<T>{
    public abstract Class<T> getMyType();
}

Zusammen mit

public class SpecificClass extends GenericClass<String>{

    @Override
    public Class<String> getMyType(){
        return String.class;
    }
}
3
PJWeisberg

Du kannst nicht Wenn Sie der Klasse eine Membervariable vom Typ T hinzufügen (Sie müssen sie nicht einmal initialisieren), können Sie diese zur Wiederherstellung des Typs verwenden.

3
andrewmu

Für den Fall, dass Sie das Speichern einer Variablen mit dem generischen Typ verwenden, können Sie dieses Problem ganz einfach lösen, indem Sie eine getClassType-Methode wie folgt hinzufügen:

public class Constant<T> {
  private T value;

  @SuppressWarnings("unchecked")
  public Class<T> getClassType () {
    return ((Class<T>) value.getClass());
  }
}

Ich verwende das bereitgestellte Klassenobjekt später, um zu überprüfen, ob es sich um eine Instanz einer bestimmten Klasse handelt, wie folgt:

Constant<?> constant = ...;
if (constant.getClassType().equals(Integer.class)) {
    Constant<Integer> integerConstant = (Constant<Integer>)constant;
    Integer value = integerConstant.getValue();
    // ...
}
2
Pablo Trinidad

Um einige der Antworten hier zu vervollständigen, musste ich den ParametrizedType von MyGenericClass abrufen, egal wie hoch die Hierarchie ist, mithilfe der Rekursion:

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());
    }
}
2
galex

Hier ist meine Lösung

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;
    }
}

Egal wie viele Ebenen Ihre Klassenhierarchie hat, diese Lösung funktioniert immer noch, z. B .:

public class FirstLevelChild<T> extends GenericClass<T> {

}

public class SecondLevelChild extends FirstLevelChild<String> {

}

In diesem Fall ist getMyType () = Java.lang.String

1
Lin Yu Cheng

Hier ist mein Trick:

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();
    }

}
1
HRgiger
public static final Class<?> getGenericArgument(final Class<?> clazz)
{
    return (Class<?>) ((ParameterizedType) clazz.getGenericSuperclass()).getActualTypeArguments()[0];
}
0
kayz1