883 Stimmen

Wie erhalte ich eine Klasseninstanz des generischen Typs T?

Ich habe eine generische Klasse, Foo<T> . Bei einer Methode der Foo Ich möchte die Klasseninstanz des Typs T aber ich kann einfach nicht anrufen T.class .

Was ist der bevorzugte Weg, um das Problem zu umgehen? T.class ?

2 Stimmen

Versuchen Sie, diese Frage zu beantworten, ich denke, es ist ähnlich. stackoverflow.com/questions/1942644/

3 Stimmen

1 Stimmen

689voto

Zsolt Török Punkte 9861

Die kurze Antwort ist, dass es keine Möglichkeit gibt, den Laufzeittyp von generischen Typparametern in Java herauszufinden. Ich empfehle die Lektüre des Kapitels über das Löschen von Typen in der Java-Tutorial für weitere Einzelheiten.

Eine gängige Lösung hierfür ist die Übergabe der Class des Typparameters in den Konstruktor des generischen Typs, z. B.

class Foo<T> {
    final Class<T> typeParameterClass;

    public Foo(Class<T> typeParameterClass) {
        this.typeParameterClass = typeParameterClass;
    }

    public void bar() {
        // you can access the typeParameterClass here and do whatever you like
    }
}

309voto

Ben Thurley Punkte 6493

Ich war auf der Suche nach einer Möglichkeit, dies selbst zu tun, ohne eine zusätzliche Abhängigkeit zum Klassenpfad hinzuzufügen. Nach einigen Untersuchungen fand ich heraus, dass es est möglich, solange Sie einen generischen Supertyp haben. Dies war für mich in Ordnung, da ich mit einem DAO Ebene mit einem generischen Ebenen-Supertyp. Wenn dies Ihr Szenario passt dann ist es die ordentlichste Ansatz IMHO.

Die meisten generischen Anwendungsfälle, die ich kenne, haben eine Art generischen Supertyp, z. B. List<T> para ArrayList<T> o GenericDAO<T> para DAO<T> , usw.

Reine Java-Lösung

Der Artikel Zugriff auf generische Typen zur Laufzeit in Java erklärt, wie man das mit reinem Java machen kann.

@SuppressWarnings("unchecked")
public GenericJpaDao() {
  this.entityBeanType = ((Class) ((ParameterizedType) getClass()
      .getGenericSuperclass()).getActualTypeArguments()[0]);
}

Frühjahrslösung

Mein Projekt verwendete Frühling was sogar noch besser ist, da Spring eine praktische Utility-Methode hat, um den Typ zu finden. Dies ist der beste Ansatz für mich, da er am saubersten aussieht. Ich schätze, wenn Sie Spring nicht verwenden würden, könnten Sie Ihre eigene Dienstprogrammmethode schreiben.

import org.springframework.core.GenericTypeResolver;

public abstract class AbstractHibernateDao<T extends DomainObject> implements DataAccessObject<T>
{

    @Autowired
    private SessionFactory sessionFactory;

    private final Class<T> genericType;

    private final String RECORD_COUNT_HQL;
    private final String FIND_ALL_HQL;

    @SuppressWarnings("unchecked")
    public AbstractHibernateDao()
    {
        this.genericType = (Class<T>) GenericTypeResolver.resolveTypeArgument(getClass(), AbstractHibernateDao.class);
        this.RECORD_COUNT_HQL = "select count(*) from " + this.genericType.getName();
        this.FIND_ALL_HQL = "from " + this.genericType.getName() + " t ";
    }

Vollständiges Code-Beispiel

Einige Leute haben in den Kommentaren damit gekämpft, dies zum Laufen zu bringen, also habe ich eine kleine Anwendung geschrieben, um beide Ansätze in Aktion zu zeigen. https://github.com/benthurley82/generic-type-resolver-test

118voto

bert bruynooghe Punkte 2819

Es gibt jedoch ein kleines Schlupfloch: Wenn Sie Ihre Foo Klasse als abstrakt. Das würde bedeuten, dass Sie Ihre Klasse instanziieren müssen als:

Foo<MyType> myFoo = new Foo<MyType>(){};

(Beachten Sie die doppelten Klammern am Ende.)

Jetzt können Sie den Typ der T zur Laufzeit:

Type mySuperclass = myFoo.getClass().getGenericSuperclass();
Type tType = ((ParameterizedType)mySuperclass).getActualTypeArguments()[0];

Beachten Sie jedoch, dass mySuperclass muss die Oberklasse der Klassendefinition sein, die den endgültigen Typ für T .

Es ist auch nicht sehr elegant, aber Sie müssen entscheiden, ob Sie lieber new Foo<MyType>(){} o new Foo<MyType>(MyType.class); in Ihrem Code.


Zum Beispiel:

import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;

import java.util.ArrayDeque;
import java.util.Deque;
import java.util.NoSuchElementException;

/**
 * Captures and silently ignores stack exceptions upon popping.
 */
public abstract class SilentStack<E> extends ArrayDeque<E> {
  public E pop() {
    try {
      return super.pop();
    }
    catch( NoSuchElementException nsee ) {
      return create();
    }
  }

  public E create() {
    try {
      Type sooper = getClass().getGenericSuperclass();
      Type t = ((ParameterizedType)sooper).getActualTypeArguments()[ 0 ];

      return (E)(Class.forName( t.toString() ).newInstance());
    }
    catch( Exception e ) {
      return null;
    }
  }
}

Dann:

public class Main {
    // Note the braces...
    private Deque<String> stack = new SilentStack<String>(){};

    public static void main( String args[] ) {
      // Returns a new instance of String.
      String s = stack.pop();
      System.out.printf( "s = '%s'\n", s );
    }
}

44voto

Andreas Dolk Punkte 110776

Ein Standardansatz/eine Standardumgehung/eine Standardlösung ist das Hinzufügen eines class Objekts an den/die Konstruktor(en), wie:

 public class Foo<T> {

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

    public Class<T> getType() {
      return type;
    }

    public T newInstance() {
      return type.newInstance();
    }
 }

23voto

Yaroslav Kovbas Punkte 380

Hier ist eine 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 <> ");
    }
} 

ANMERKUNGEN: Kann nur als Superklasse verwendet werden

  1. muss um eine typisierte Klasse erweitert werden ( Child extends Generic<Integer> )

OR

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

CodeJaeger.com

CodeJaeger ist eine Gemeinschaft für Programmierer, die täglich Hilfe erhalten..
Wir haben viele Inhalte, und Sie können auch Ihre eigenen Fragen stellen oder die Fragen anderer Leute lösen.

Powered by:

X