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
?
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
?
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
}
}
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.
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]);
}
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 ";
}
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
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 );
}
}
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();
}
}
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
Child extends Generic<Integer>
)OR
new Generic<Integer>() {};
) 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.
2 Stimmen
Versuchen Sie, diese Frage zu beantworten, ich denke, es ist ähnlich. stackoverflow.com/questions/1942644/
3 Stimmen
Mögliche Duplikate von Generischen Typ einer Klasse zur Laufzeit abrufen
1 Stimmen
Mögliche Duplikate von Instanziierung einer generischen Klasse in Java
2 Stimmen
import com.fasterxml.jackson.core.type.TypeReference;
new TypeReference<T>(){}
0 Stimmen
Ja, wie der obige Kommentar andeutet, habe ich das Problem durch die Bereitstellung von
new TypeReference<Foo<Bar>>() {}
als Parameter für meine deserialisierte Modelllesemethode.