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.
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;
}
}
Ich habe so etwas gesehen
private Class<T> persistentClass;
public Constructor() {
this.persistentClass = (Class<T>) ((ParameterizedType) getClass()
.getGenericSuperclass()).getActualTypeArguments()[0];
}
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.
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.
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:
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.
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;
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();
}
}
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;
}
}
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
public abstract class AbstractDao<T>
{
private final Class<T> persistentClass;
public AbstractDao()
{
this.persistentClass = (Class<T>) ((ParameterizedType) this.getClass().getGenericSuperclass())
.getActualTypeArguments()[0];
}
}
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);
}
}
}
}
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:
PassedGenericType
) in ZWEI Orten an und nicht in einer, die nicht DRY ist.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>() {};
)
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());
}
}
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;
}
}
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.
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();
// ...
}
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());
}
}
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
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();
}
}
public static final Class<?> getGenericArgument(final Class<?> clazz)
{
return (Class<?>) ((ParameterizedType) clazz.getGenericSuperclass()).getActualTypeArguments()[0];
}