Holen Sie sich zur Laufzeit einen generischen Klassentyp

508

Wie kann ich das erreichen?

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

Alles, was ich bisher versucht habe, gibt immer den Typ zurück Objectund nicht den spezifischen verwendeten Typ.

Glenn
quelle
3
@SamuelVidal Dies ist nicht .NET, dies ist Java.
Frontear

Antworten:

317

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) Problemumgehungsmuster:

public class GenericClass<T> {

     private final Class<T> type;

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

     public Class<T> getMyType() {
         return this.type;
     }
}
Henning
quelle
58
Ich mag diese Antwort, aber das Instanziieren ist etwas umständlich: GenericClass <AnotherClass> g = new GenericClass <AnotherClass> (AnotherClass.class);
Eliseo Ocampos
1
Es ist noch ausführlicher, wenn Sie einen Dao / Factory / Manager-Ansatz verwenden. Foo foo1 = GetDao<Foo>(Foo.class).get(Foo.class, 1)
DJJJ
2
Das ist wahr, funktioniert aber nicht in allen Fällen wie zustandslose Remote-Beans, die durch Container / Reflektion instanziiert werden.
DJJJ
6
Nur als Folge meines vorherigen Kommentars - nachdem ich viel Schmerz mit dem Nachdenken gespielt hatte, benutzte ich diese Antwort.
Tomáš Zato - Wiedereinsetzung von Monica
18
Sie können die überflüssige Referenz umgehen, indem Sie eine generische statische Factory-Methode bereitstellen. So etwas wie public static <T> GenericClass<T> of(Class<T> type) {...}und nenne es dann so : GenericClass<String> var = GenericClass.of(String.class). Ein bisschen schöner.
Joeri Hendrickx
262

Ich habe so etwas gesehen

private Class<T> persistentClass;

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

im Beispiel für den Ruhezustand von GenericDataAccessObjects

FrVaBe
quelle
84
Diese Technik funktioniert dort, wo der Typparameter in der unmittelbaren Oberklasse definiert ist. Sie schlägt jedoch fehl, wenn der Typparameter an anderer Stelle in der Typhierarchie definiert ist. Für die Behandlung komplexerer Fälle kann beispielsweise TypeTools verwendet werden. Die Dokumente enthalten ein Beispiel für ein komplexeres generisches DAO.
Jonathan
25
Dies gibt nur die tatsächlichen Typparameter zurück, die verwendet werden, wenn eine KLASSE etwas implementiert / erweitert, das generische Deklarationen enthält. Es werden nicht die tatsächlichen Typparameter zurückgegeben, die verwendet werden, wenn eine INSTANZ instanziiert wird. Mit anderen Worten, es KANN sagen, dass in class A implements Comparable<String>der tatsächliche Typparameter ist String, aber es kann NICHT sagen, dass in Set<String> a = new TreeSet<String>()der tatsächliche Typparameter ist String. Tatsächlich werden die Typparameterinformationen nach dem Kompilieren "gelöscht", wie in anderen Antworten erläutert.
Siu Ching Pong -Asuka Kenji-
32
Ich bekomme java.lang.Class cannot be cast to java.lang.reflect.ParameterizedTypefür diese Antwort.
Tomáš Zato - Wiedereinsetzung Monica
4
Dieser Ansatz kann auch Class-Matevon den Jackson-Leuten erreicht werden. Ich habe hier eine Zusammenfassung geschrieben: gist.github.com/yunspace/930d4d40a787a1f6a7d1
yunspace
5
@ TomášZato Das einfache Aufrufen des obigen Codes ergab für mich die gleiche Ausnahme. Ich weiß, dass es etwas spät ist, aber in meinem Fall musste ich trotzdem aufrufen (Class<T>) ((ParameterizedType)getClass().getSuperclass().getGenericSuperclass()).getActualTypeArguments(), um zu den tatsächlichen Typargumenten zu gelangen.
AndrewMcCoist
99

Generika werden zur Laufzeit nicht überprüft . Dies bedeutet, dass die Informationen zur Laufzeit nicht vorhanden sind.

Das Hinzufügen von Generika zu Java unter Wahrung der Abwärtskompatibilität war eine Tour de Force (Sie können das wegweisende Papier darüber lesen: Die Zukunft für die Vergangenheit sicher machen: der Java-Programmiersprache Generizität hinzufügen ).

Es gibt eine reiche Literatur zu diesem Thema, und einige Leute sind mit dem aktuellen Zustand unzufrieden , andere sagen, dass es tatsächlich ein Köder ist und es keinen wirklichen Bedarf dafür gibt. Sie können beide Links lesen, ich fand sie ziemlich interessant.

ewernli
quelle
179
Natürlich sind wir unzufrieden, .NET hat einen viel besseren generischen Handhabungsmechanismus
Pacerier
6
@Pacerier: Aber reifizierte Generika allein würden Java nicht auf die Ebene von .NET bringen. Werttypen Ein spezieller Code für diese ist mindestens ebenso wichtig, warum .NET im Bereich Generika besser ist.
Joachim Sauer
@ JoachimSauer, ja Werttypen. Die wollte ich schon immer in Java. Übrigens, was meinst du mit spezialisiertem Code?
Pacerier
3
@ spaaarky21 Nein, generische Typparameter werden beim Kompilieren entfernt (sogenanntes "Löschen", Sie können es googeln). Der Trick in FrVaBes Antwort funktioniert nur, wenn die Typparameter der Oberklasse statisch bekannt sind (siehe den ersten Kommentar von Johnathn)
ewernli
1
Das Löschen vom Java-Typ ist ein historischer Konstruktionsfehler. Es wurde mehr Code geschrieben, um das Problem zu umgehen, als zur Implementierung geschrieben wurde.
Abhijit Sarkar
68

Benutze 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 geschrieben , einige Voll fledge Beispiele einschließlich abstrakten Klassen und Unterklassen hier .

Hinweis: Dazu müssen Sie eine Unterklasse instanziieren vonGenericClass damit der Typparameter korrekt gebunden werden kann. Andernfalls wird nur der Typ als zurückgegeben T.

Cody A. Ray
quelle
3
Beachten Sie, dass ich eine leere anonyme Unterklasse erstelle (siehe die beiden geschweiften Klammern am Ende). Dies verwendet Reflexion, um das Löschen des Laufzeittyps von Java zu bekämpfen. Sie können hier mehr erfahren: code.google.com/p/guava-libraries/wiki/ReflectionExplained
Cody A. Ray
3
@ CodyA.Ray Dein Code wirft a java.lang.IllegalArgumentException: class com.google.common.reflect.TypeToken isn't parameterized. Also habe ich die Zeile new TypeToken(getClass()) { }auf geändert new TypeToken<T>(getClass()) { }. Jetzt läuft der Code einwandfrei, aber Type ist immer noch 'T'. Siehe dies: gist.github.com/m-manu/9cda9d8f9d53bead2035
Manu Manjunath
3
@Dominik Bitte sehen Sie sich das aktualisierte Beispiel an, das Sie kopieren und einfügen können, um sich selbst zu testen. Ich habe auch einen Hinweis hinzugefügt, der klarstellt, dass Sie eine Unterklasse instanziieren müssen (wie gezeigt). Lesen Sie als allgemeine Etikette-Empfehlung alle verlinkten Artikel und verwandten Javadocs, bevor Sie ein Poster des "Wunschdenkens" beschuldigen. Ich habe mehrmals ähnlichen Produktionscode verwendet. Die Guaven-Helfer, die ich demonstriere, sind genau für diesen Anwendungsfall gedacht und ihre Javadocs zeigen fast eine genaue Antwort auf diese Frage. docs.guava-libraries.googlecode.com/git/javadoc/com/google/…
Cody A. Ray
1
Diese Antwort funktioniert hervorragend mit Java8 - Ich habe eine Schnittstelle und eine Factory-Klasse, um verschiedene Typen auszugeben, und wollte, dass Benutzer den Typ leicht bestimmen können. In meiner Oberfläche habe ich die Standardmethode hinzugefügt: default Type getParameterType() { final TypeToken<T> typeToken = new TypeToken<T>(getClass()) {}; final Type type = typeToken.getType(); return type; }
Richard Sand
2
@ CodyA.Ray Da dies nur mit Unterklassen von funktioniert GenericClass, sollten Sie diese Klasse abstractso gestalten , dass keine falsche Verwendung kompiliert wird.
Martin
37

Sicher kannst du.

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

Aus der offiziellen API:

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

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

Joeri Hendrickx
quelle
10
getActualTypeArguments gibt nur die Typargumente für die unmittelbare Klasse zurück. Wenn Sie eine komplexe Typhierarchie haben, in der T an einer beliebigen Stelle in der Hierarchie parametrisiert werden kann, müssen Sie ein wenig arbeiten, um herauszufinden, was es ist. Dies ist mehr oder weniger das, was TypeTools tut.
Jonathan
36

Java-Generika haben meistens Kompilierungszeit. Dies bedeutet, dass die Typinformationen zur Laufzeit verloren gehen.

class GenericCls<T>
{
    T t;
}

wird zu 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;
josefx
quelle
9
private final Class<T> type;
NaXa
Wie kann ich einen Array-Typ daraus erstellen:Type t = //String[]
Pawel Cioch
@PawelCioch java.lang.reflect.Array.newInstance (Elementtyp, Länge);
Ich
@PawelCioch hat eine .getClass () verpasst, um den Typ aus dem erstellten Array abzurufen. Es scheint keinen direkten Weg zu geben, eine Array-Klasse zu erhalten. Die meisten Java-Sammlungen verwenden stattdessen nur Object [].
Josefx
23
public abstract class AbstractDao<T>
{
    private final Class<T> persistentClass;

    public AbstractDao()
    {
        this.persistentClass = (Class<T>) ((ParameterizedType) this.getClass().getGenericSuperclass())
                .getActualTypeArguments()[0];
    }
}
Ali Yeganeh
quelle
3
Ich stimme dieser Antwort zu, weil es eine Lösung ist, die für die gestellte Frage funktioniert. Für diejenigen, die wie ich mit mehr als einer generischen Klasse in der Klassenhierarchie nach oben navigieren möchten, funktioniert dies jedoch nicht. Weil Sie java.lang.object anstelle der tatsächlichen Klasse erhalten.
KMC
3
Bitte beachten Sie, dass diese Lösung NUR funktioniert, wenn die Klasse, die den generischen Typ enthält, ABSTRACT
BabaNew
Arbeitete
@JRA_TLL du hast anscheinend etwas falsch gemacht. Ich habe es gerade mit Java 12 verwendet und funktioniert wie ein Zauber.
Googie
Wenn Sie in der Ansichtshierarchie nach oben navigieren möchten, können Sie die genericSuperclass in Class <*> umwandeln und die genericSuperclass abrufen. Am besten in einer Schleife.
Fupduck
21

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();
    }
}
Moesio
quelle
9
Ich habe "Ausnahme im Thread" main "java.lang.ClassCastException: java.lang.Class kann nicht mit diesem Beispielcode in java.lang.reflect.ParameterizedType umgewandelt werden
yuyang
16

Ich glaube nicht, dass Sie das können. Java verwendet beim Kompilieren das Löschen von Typen, damit Ihr Code mit Anwendungen und Bibliotheken kompatibel ist, die vor Generika erstellt wurden.

Aus den Oracle-Dokumenten:

Geben Sie Erasure ein

Generika wurden in die Java-Sprache eingeführt, um beim Kompilieren strengere Typprüfungen zu ermöglichen und die generische Programmierung zu unterstützen. Um Generika zu implementieren, wendet der Java-Compiler das Löschen des Typs an:

Ersetzen Sie alle Typparameter in generischen Typen durch ihre Grenzen oder Objekte, wenn die Typparameter unbegrenzt sind. Der erzeugte Bytecode enthält daher nur gewöhnliche Klassen, Schnittstellen und Methoden. Fügen Sie bei Bedarf Gussteile ein, um die Sicherheit des Typs zu gewährleisten. Generieren Sie Brückenmethoden, um den Polymorphismus in erweiterten generischen Typen zu erhalten. Durch das Löschen von Typen wird sichergestellt, dass für parametrisierte Typen keine neuen Klassen erstellt werden. Folglich verursachen Generika keinen Laufzeitaufwand.

http://docs.oracle.com/javase/tutorial/java/generics/erasure.html

Dimitar
quelle
Jup, das ist unmöglich. Java würde reified Generics benötigen, damit dies funktioniert.
Henning
15

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

Kurzes schnelles 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;
    }
  }
Ondrej Bozek
quelle
3
In welcher bestimmten Zeile in diesem Code werden aktuelle Parameter vom Typ Laufzeit gelesen?
Ivan Matavulj
Hier? Type[] actualTypeArguments = parameterizedType.getActualTypeArguments();
Ondrej Bozek
9

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

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

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

Definieren Sie zunächst eine benutzerdefinierte Anmerkung in dieser Richtung:

import java.lang.annotation.*;

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

Anschließend müssen Sie die Anmerkung 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) an ZWEI Stellen an und nicht an einer Stelle, die nicht trocken ist.
  2. Dies ist nur möglich, wenn Sie die konkreten Unterklassen ändern können.
DaBlick
quelle
Ja, es ist nicht trocken, aber es ist sauberer als der oben vorgeschlagene Erweiterungsansatz. Ich mochte es. danke
agodinhost
8

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);
      }
    }
  }
}
Matthias M.
quelle
10
Dies ist keine Antwort auf diese Frage.
Adam Arold
1
Mein Code ist nicht die genaue Lösung für die Frage. Es gibt die generischen Typparameter der Klasse zurück, aber nicht den tatsächlichen Typ von T. Aber es kann für andere hilfreich sein, die über die Frage stolpern und nach meiner Lösung suchen.
Matthias M
1
getClass (). getGenericSuperclass () erzielt den gleichen Effekt.
Gorki
5

Hier ist funktionierende 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 typed class ( Child extends Generic<Integer>) erweitert werden
ODER

2. Muss als anonyme Implementierung erstellt werden ( new Generic<Integer>() {};)

Jaroslaw Kowbas
quelle
4

Das kannst du nicht. Wenn Sie der Klasse eine Mitgliedsvariable vom Typ T hinzufügen (Sie müssen sie nicht einmal initialisieren), können Sie damit den Typ wiederherstellen.

andrewmu
quelle
2
Ups, ok. Sie müssen es von einem Konstruktor aus initialisieren.
Andrewmu
4

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;
    }
}
PJWeisberg
quelle
4
Dies funktioniert technisch, löst jedoch nicht den allgemeinen Fall, und ich denke, das ist es, wonach das Originalplakat sucht.
ggb667
Dies verdient es nicht, wie bisher abgewählt zu werden - das Originalplakat war nicht explizit. Diese Antwort bietet ein Entwurfsmuster, das funktioniert und einfach zu implementieren ist, vorausgesetzt, es ist geeignet, die generische Klasse abstrakt zu machen.
VirtualMichael
4

Eine einfache Lösung für diese Kabine 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());
    }
}
Paul92
quelle
3

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

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

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

}
HRgiger
quelle
1
Hinweis: Dies funktioniert nicht, wenn Tes sich um eine Typvariable handelt. In dem Fall, dass Tes sich um eine Typvariable handelt, erstellen die Varargs ein Array zum Löschen von T. Siehe z . B. http://ideone.com/DIPNwd .
Radiodef
1
Dies gibt "Objekt" zurück
yahya
Vielleicht versuchen Sie, eine andere Frage zu beantworten
Khan
1

Nur für den Fall, dass Sie eine Variable mit dem generischen Typ speichern, können Sie dieses Problem leicht 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 eine Instanz einer bestimmten Klasse ist, wie folgt:

Constant<?> constant = ...;
if (constant.getClassType().equals(Integer.class)) {
    Constant<Integer> integerConstant = (Constant<Integer>)constant;
    Integer value = integerConstant.getValue();
    // ...
}
Pablo Trinidad
quelle
2
Das ist leider problematisch. Was valueist , wenn null? Zweitens, was valueist , wenn es sich um eine Unterklasse von handelt T? Constant<Number> c = new Constant<Number>(new Integer(0)); Class<Number> n = c.getClassType();kehrt zurück, Integer.classwenn es zurückkehren soll Number.class. Es wäre richtiger, zurückzukehren Class<? extends T>. Integer.class ist ein Class<? extends Number> aber kein Class<Number>.
Radiodef
1

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

Unabhängig davon, wie viele Ebenen Ihre Klassenhierarchie hat, funktioniert diese Lösung immer noch, zum Beispiel:

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

}

public class SecondLevelChild extends FirstLevelChild<String> {

}

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

Lin Yu Cheng
quelle
Dies gibt nicht den Typ von T zurück. Es gibt T zurück, nicht java.lang.String, außer dass der Code den Typ nicht in Klasse <T> verdeckt
Mohy Eldeen
Hier ist ein Online-Beispiel, das ich gemacht habe. Klicken Sie auf Kompilieren und ausführen, um das Ergebnis zu erhalten. tutorialspoint.com/…
Lin Yu Cheng
Funktioniert für mich - als WildFly Weld CDI eine alternative Methode brach.
Andre
Ich habeException in thread "main" java.lang.NullPointerException at Main$ClassA.findTypeArguments(Main.java:54) at Main$ClassA.findTypeArguments(Main.java:54) at Main$ClassA.findTypeArguments(Main.java:54) at Main$ClassA.<init>(Main.java:43) at Main.main(Main.java:61)
Yuyang
0
public static final Class<?> getGenericArgument(final Class<?> clazz)
{
    return (Class<?>) ((ParameterizedType) clazz.getGenericSuperclass()).getActualTypeArguments()[0];
}
kayz1
quelle
0

Hier ist meine Lösung. Die Beispiele sollten es erklären. Die einzige Voraussetzung ist, dass eine Unterklasse den generischen Typ und kein Objekt festlegen muss.

import java.lang.reflect.AccessibleObject;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.lang.reflect.TypeVariable;
import java.util.HashMap;
import java.util.Map;

public class TypeUtils {

    /*** EXAMPLES ***/

    public static class Class1<A, B, C> {

        public A someA;
        public B someB;
        public C someC;

        public Class<?> getAType() {
            return getTypeParameterType(this.getClass(), Class1.class, 0);
        }

        public Class<?> getCType() {
            return getTypeParameterType(this.getClass(), Class1.class, 2);
        }
    }

    public static class Class2<D, A, B, E, C> extends Class1<A, B, C> {

        public B someB;
        public D someD;
        public E someE;
    }

    public static class Class3<E, C> extends Class2<String, Integer, Double, E, C> {

        public E someE;
    }

    public static class Class4 extends Class3<Boolean, Long> {

    }

    public static void test() throws NoSuchFieldException {

        Class4 class4 = new Class4();
        Class<?> typeA = class4.getAType(); // typeA = Integer
        Class<?> typeC = class4.getCType(); // typeC = Long

        Field fieldSomeA = class4.getClass().getField("someA");
        Class<?> typeSomeA = TypeUtils.getFieldType(class4.getClass(), fieldSomeA); // typeSomeA = Integer

        Field fieldSomeE = class4.getClass().getField("someE");
        Class<?> typeSomeE = TypeUtils.getFieldType(class4.getClass(), fieldSomeE); // typeSomeE = Boolean


    }

    /*** UTILS ***/

    public static Class<?> getTypeVariableType(Class<?> subClass, TypeVariable<?> typeVariable) {
        Map<TypeVariable<?>, Type> subMap = new HashMap<>();
        Class<?> superClass;
        while ((superClass = subClass.getSuperclass()) != null) {

            Map<TypeVariable<?>, Type> superMap = new HashMap<>();
            Type superGeneric = subClass.getGenericSuperclass();
            if (superGeneric instanceof ParameterizedType) {

                TypeVariable<?>[] typeParams = superClass.getTypeParameters();
                Type[] actualTypeArgs = ((ParameterizedType) superGeneric).getActualTypeArguments();

                for (int i = 0; i < typeParams.length; i++) {
                    Type actualType = actualTypeArgs[i];
                    if (actualType instanceof TypeVariable) {
                        actualType = subMap.get(actualType);
                    }
                    if (typeVariable == typeParams[i]) return (Class<?>) actualType;
                    superMap.put(typeParams[i], actualType);
                }
            }
            subClass = superClass;
            subMap = superMap;
        }
        return null;
    }

    public static Class<?> getTypeParameterType(Class<?> subClass, Class<?> superClass, int typeParameterIndex) {
        return TypeUtils.getTypeVariableType(subClass, superClass.getTypeParameters()[typeParameterIndex]);
    }

    public static Class<?> getFieldType(Class<?> clazz, AccessibleObject element) {
        Class<?> type = null;
        Type genericType = null;

        if (element instanceof Field) {
            type = ((Field) element).getType();
            genericType = ((Field) element).getGenericType();
        } else if (element instanceof Method) {
            type = ((Method) element).getReturnType();
            genericType = ((Method) element).getGenericReturnType();
        }

        if (genericType instanceof TypeVariable) {
            Class<?> typeVariableType = TypeUtils.getTypeVariableType(clazz, (TypeVariable) genericType);
            if (typeVariableType != null) {
                type = typeVariableType;
            }
        }

        return type;
    }

}
PetermaxX
quelle
-1

Ich habe das gleiche gemacht wie @Moesio Above, aber in Kotlin könnte es so gemacht werden:

class A<T : SomeClass>() {

    var someClassType : T

    init(){
    this.someClassType = (javaClass.genericSuperclass as ParameterizedType).actualTypeArguments[0] as Class<T>
    }

}
Bonestack
quelle
-4

Es könnte für jemanden nützlich sein. Sie können java.lang.ref.WeakReference verwenden. diesen Weg:

class SomeClass<N>{
  WeakReference<N> variableToGetTypeFrom;

  N getType(){
    return variableToGetTypeFrom.get();
  }
}
TheLetch
quelle
Wie soll diese Klasse verwendet werden? Warum WeakReference? Bitte geben Sie eine Erklärung mit Ihrer Antwort, nicht nur einen Code.
Abhijit Sarkar
Wenn Sie also eine haben SomeClass<MyClass> , können Sie diese Instanz instanziieren SomeClassund aufrufen getTypeund haben die Laufzeit MyClass.
TheLetch
Sicher, aber warum WeakReference? Was Sie gesagt haben, unterscheidet sich nicht von den meisten anderen Antworten.
Abhijit Sarkar
Erstens ist mein Ansatz kürzer (weniger Code), zweitens verhindern die schwachen Referenzen nicht, dass ihre Referenten finalisierbar werden, und soweit ich weiß, wird keine Reflexion verwendet, daher ist es schnell
TheLetch
Dies gilt nicht die Art von etwas zu bekommen, das gibt ein Objekt dieser Art, die zu ihrer Information, die Sie buchstäblich jede Art von Wrapper tun können ( AtomicReference, List, Set).
Frontear
-5

Ich fand, dass dies eine einfache verständliche und leicht erklärbare Lösung ist

public class GenericClass<T> {

    private Class classForT(T...t) {
        return t.getClass().getComponentType();
    }

    public static void main(String[] args) {
        GenericClass<String> g = new GenericClass<String>();

        System.out.println(g.classForT());
        System.out.println(String.class);
    }
}
Mark Karchner
quelle
Erklären Sie (T...t). (Deshalb funktioniert dieser Code nicht.)
Christopher Schneider