ChatGPT解决这个技术问题 Extra ChatGPT

在运行时获取类的泛型类型

我怎样才能做到这一点?

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

到目前为止,我尝试过的所有操作都返回类型 Object 而不是使用的特定类型。


H
Henning

正如其他人所提到的,只有在某些情况下通过反射才有可能。

如果您真的需要该类型,这是通常的(类型安全)解决方法模式:

public class GenericClass<T> {

     private final Class<T> type;

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

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

我喜欢这个答案,但实例化有点麻烦: GenericClass g = new GenericClass(AnotherClass.class);
如果您使用 dao/factory/manager 方法,它会更加冗长。 Foo foo1 = GetDao<Foo>(Foo.class).get(Foo.class, 1)
没错,但并非在所有情况下都有效,例如由容器/反射实例化的无状态远程 bean。
就像我之前评论的后续行动一样——在经历了很多痛苦的反思之后,我最终使用了这个答案。
您可以通过提供通用静态工厂方法来绕过多余的引用。类似 public static <T> GenericClass<T> of(Class<T> type) {...} 的东西,然后这样称呼它:GenericClass<String> var = GenericClass.of(String.class)。好看一点。
F
FrVaBe

我见过这样的事情

private Class<T> persistentClass;

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

hibernate GenericDataAccessObjects 示例中


此技术适用于在直接超类上定义类型参数的情况,但如果类型参数在类型层次结构中的其他位置定义,则它会失败。为了处理更复杂的情况,可以使用 TypeTools 之类的东西。这些文档包括一个更复杂的通用 DAO 的示例。
这仅返回 CLASS 实现/扩展具有泛型声明的内容时使用的实际类型参数,它不返回实例化 INSTANCE 时使用的实际类型参数。换句话说,它 CAN 告诉在 class A implements Comparable<String> 中,实际类型参数是 String,但它 CANNOT 告诉在 Set<String> a = new TreeSet<String>() 中,实际类型参数是 String。事实上,类型参数信息在编译后被“擦除”,如其他答案中所述。
我得到java.lang.Class cannot be cast to java.lang.reflect.ParameterizedType这个答案。
这种方法也可以使用 Jackson 的 Class-Mate 来实现。我在这里写了一个要点gist.github.com/yunspace/930d4d40a787a1f6a7d1
@TomášZato 简单地调用上面的代码为我返回了相同的异常。我知道这有点晚了,但无论如何,就我而言,我必须调用 (Class<T>) ((ParameterizedType)getClass().getSuperclass().getGenericSuperclass()).getActualTypeArguments() 才能获得实际的类型参数。
J
Jacob van Lingen

泛型在运行时没有具体化。这意味着信息在运行时不存在。

在保持向后兼容性的同时向 Java 添加泛型是一项艰巨的任务(您可以查看有关它的开创性论文:Making the future safe for the past: adding genericity to the Java programming language)。

关于这个主题有丰富的文献,有些人对当前状态dissatisfied,有些人说实际上它是一个lure并且没有真正需要它。你可以阅读这两个链接,我发现它们很有趣。


我们当然不满意,.NET 有更好的通用处理机制
@Pacerier:但是单独的具体化泛型不会将 Java 带到 .NET 的水平。值类型的专用代码至少对于 .NET 在泛型领域更好的原因同样重要。
@JoachimSauer,是的值类型。我一直想要那些在java中的。顺便说一句,您所说的专用代码是什么意思?
@spaarky21 不,泛型类型参数在编译期间被删除(所谓的“擦除”,你可以谷歌它)。 FrVaBe 答案中的技巧仅在静态已知超类的类型参数时才有效(请参阅 Johnathn 的第一条评论)
Java 类型擦除是一个历史设计缺陷;为绕过它而编写的代码比为实现它而编写的代码多。
C
Cody A. Ray

使用番石榴。

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

不久前,我发布了一些完整的示例,包括抽象类和子类 here

注意:这要求您实例化 GenericClass子类,以便它可以正确绑定类型参数。否则它只会将类型返回为 T


构造函数 TypeToken(Type) 不可见
请注意,我创建了一个空的匿名子类(请参见末尾的两个花括号)。这使用反射来对抗 Java 的运行时类型擦除。您可以在此处了解更多信息:code.google.com/p/guava-libraries/wiki/ReflectionExplained
@CodyA.Ray 您的代码抛出 java.lang.IllegalArgumentException: class com.google.common.reflect.TypeToken isn't parameterized。所以我将第 new TypeToken(getClass()) { } 行更改为 new TypeToken<T>(getClass()) { }。现在,代码运行良好,但 Type 仍然是“T”。看到这个:gist.github.com/m-manu/9cda9d8f9d53bead2035
@Dominik 请查看更新的示例,您可以复制并粘贴以测试自己。我还添加了一条说明,说明您必须实例化一个子类(如图所示)。作为一般礼仪建议,请在指责海报“一厢情愿”之前阅读任何链接的文章和相关的 javadocs。我多次使用类似的生产代码。我正在演示的 Guava 助手适用于这个确切的用例,他们的 javadocs 几乎可以准确地回答这个问题。 docs.guava-libraries.googlecode.com/git/javadoc/com/google/…
@CodyA.Ray 由于这只适用于 GenericClass 的子类,因此您应该将该类设为 abstract,这样错误的用法就不会编译。
j
josefx

Java 泛型大多是编译时的,这意味着类型信息在运行时会丢失。

class GenericCls<T>
{
    T t;
}

将被编译成类似

class GenericCls
{
   Object o;
}

要在运行时获取类型信息,您必须将其添加为 ctor 的参数。

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

例子:

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

private final Class<T> type;
如何从中创建数组类型:Type t = //String[]
@PawelCioch java.lang.reflect.Array.newInstance(elementtype, length);希望这会有所帮助(javadoc 可以在这里找到 docs.oracle.com/javase/8/docs/api/java/lang/reflect/…
@PawelCioch 错过了从创建的数组中获取类型的 .getClass() 。似乎没有直接的方法来获取数组类。大多数 Java 集合只是使用 Object[] 代替。
j
jpaugh

你当然可以。

出于向后兼容性的原因,Java 在运行时不使用这些信息。但是这些信息实际上是作为元数据存在的,可以通过反射访问(但它仍然不用于类型检查)。

来自官方 API:

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

但是,对于您的情况,我不会使用反射。我个人更倾向于将其用于框架代码。在您的情况下,我只需将类型添加为构造函数参数。


getActualTypeArguments 只返回直接类的类型参数。如果您有一个复杂的类型层次结构,其中 T 可以在层次结构中的任何位置参数化,您需要做一些工作来弄清楚它是什么。这或多或少是 TypeTools 所做的。
可以通过添加一些上下文或代码示例来改进这个答案。我在文档中看到了方法,我可以通过我的 IDE 的调试器找到它actualTypeArguments,但我不知道如何实际访问它。
D
DevDio
public abstract class AbstractDao<T>
{
    private final Class<T> persistentClass;

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

我赞成这个答案,因为它是一个适用于所提出问题的解决方案。但是,对于像我这样想要在具有多个通用类的类层次结构中向上导航的人来说,这是行不通的。因为您将获得 java.lang.object 而不是实际的类。
请注意,此解决方案仅在拥有泛型类型的类是 ABSTRACT 时才有效
@JRA_TLL 你显然做错了什么。我只是将它与 Java 12 一起使用,并且效果很好。
如果您想在视图层次结构中向上导航,您可以将 genericSuperclass 转换为 Class<*> 并获取 genericSuperclass。最好在一个循环中。
M
Moesio

我使用了以下方法:

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

使用此示例代码,我得到“线程“主”java.lang.ClassCastException:java.lang.Class 无法转换为 java.lang.reflect.ParameterizedType 中的异常”
D
Dimitar

我不认为您可以,Java 在编译时使用类型擦除,因此您的代码与创建前泛型的应用程序和库兼容。

来自 Oracle 文档:

将类型擦除泛型引入 Java 语言以在编译时提供更严格的类型检查并支持泛型编程。为了实现泛型,Java 编译器将类型擦除应用于: 将泛型类型中的所有类型参数替换为其边界或 Object(如果类型参数是无界的)。因此,生成的字节码只包含普通的类、接口和方法。必要时插入类型转换以保持类型安全。生成桥方法以保留扩展泛型类型中的多态性。类型擦除确保不会为参数化类型创建新类;因此,泛型不会产生运行时开销。

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


朱,这是不可能的。 Java 需要具体化的泛型才能工作。
完全有可能,请参阅其他多个答案。
O
Ondrej Bozek

article by Ian Robertson 中描述的技术对我有用。

简而言之,快速而肮脏的例子:

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

在这段代码中的哪一行是正在读取的实际运行时类型参数?
这里? Type[] actualTypeArguments = parameterizedType.getActualTypeArguments();
D
DaBlick

我认为还有另一个优雅的解决方案。

您想要做的是(安全地)将泛型类型参数的类型从具体类“传递”到超类。

如果您允许自己将类类型视为类上的“元数据”,则建议使用 Java 方法在运行时对元数据进行编码:注解。

首先按照这些思路定义一个自定义注解:

import java.lang.annotation.*;

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

然后,您必须将注释添加到您的子类。

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

然后您可以使用此代码获取基类中的类类型:

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

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

    return ne.entityClass();
}

这种方法的一些限制是:

您在两个地方而不是一个非 DRY 的地方指定泛型类型 (PassedGenericType)。这只有在您可以修改具体子类时才有可能。


是的,它不是 DRY,但它比上面建议的扩展方法更干净。我喜欢它。谢谢
P
PJWeisberg

这是一种方法,我不得不使用一次或两次:

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

随着

public class SpecificClass extends GenericClass<String>{

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

这在技术上是可行的,但它并不能解决一般情况,我认为这就是原始海报所追求的。
这不应该像现在一样被否决 - 原始海报没有明确。这个答案提供了一种可以工作且易于实现的设计模式,只要它适合使泛型类抽象。
M
Matthias M

这是我的解决方案:

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

这不是这个问题的答案。
我的代码不是该问题的确切解决方案。它返回类的泛型类型参数,但不是 T 的实际类型。但它可能对其他遇到问题并正在寻找我的解决方案的人有所帮助。
getClass().getGenericSuperclass() 会达到同样的效果。
Y
Yaroslav Kovbas

这是有效的解决方案!!!

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

注意:只能用作超类
1。必须使用类型化类 (Child extends Generic<Integer>)
OR

2 进行扩展。必须创建为匿名实现 (new Generic<Integer>() {};)


ClassCastException
a
andrewmu

你不能。如果您将 T 类型的成员变量添加到类中(您甚至不必初始化它),您可以使用它来恢复类型。


哎呀,好的。您必须从构造函数初始化它。
p
perror

该驾驶室的一个简单解决方案如下

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

这需要传递一个可能并不总是可行的对象。
g
galex

为了完成这里的一些答案,我必须借助递归获得 MyGenericClass 的 ParametrizedType,无论层次结构有多高:

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

ClassCastException
L
Lin Yu Cheng

这是我的解决方案

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

无论您的类层次结构有多少级别,此解决方案仍然有效,例如:

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

}

public class SecondLevelChild extends FirstLevelChild<String> {

}

在这种情况下,getMyType() = java.lang.String


这不是返回 T 的类型。它返回 T 而不是 java.lang.String 除了代码未能将 Type 转换为 Class
这是我制作的在线示例。点击编译并执行,即可得到结果。 tutorialspoint.com/…
对我有用 - 当 WildFly Weld CDI 打破了另一种方法时。
我得到了Exception 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)
无论您的类层次结构有多少级别,但停止使用泛型的一级类......
H
HRgiger

这是我的窍门:

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

}

注意:当 T 是类型变量时,此不起作用。在 T 是类型变量的情况下,可变参数创建一个擦除 T 的数组。参见例如 http://ideone.com/DIPNwd
这将返回“对象”
可能是您正在尝试回答其他问题🤔
P
Pablo Trinidad

万一您使用泛型类型存储变量,您可以通过添加 getClassType 方法轻松解决此问题,如下所示:

public class Constant<T> {
  private T value;

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

我稍后使用提供的类对象来检查它是否是给定类的实例,如下所示:

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

不幸的是,这是有问题的。首先,如果 valuenull 怎么办?其次,如果 valueT 的子类怎么办? Constant<Number> c = new Constant<Number>(new Integer(0)); Class<Number> n = c.getClassType(); 在应该返回 Number.class 时返回 Integer.class。返回 Class<? extends T> 会更正确。 Integer.class 是一个 Class<? extends Number> 但不是 Class<Number>
P
PetermaxX

这是我的解决方案。这些例子应该解释它。唯一的要求是子类必须设置泛型类型,而不是对象。

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

}

什么是 TypeUtils?
k
kayz1
public static final Class<?> getGenericArgument(final Class<?> clazz)
{
    return (Class<?>) ((ParameterizedType) clazz.getGenericSuperclass()).getActualTypeArguments()[0];
}

C
CoolMind

如果你有这样的课程:

public class GenericClass<T> {
    private T data;
}

使用 T 变量,然后您可以打印 T 名称:

System.out.println(data.getClass().getSimpleName()); // "String", "Integer", etc.

我试过这个并得到 NullObject 而不是泛型类型。
@nasch,也许是 data == null。在这种情况下,我们无法获得类型。
正是如此。如果您已经有该类型的实例,那么获取它的类是没有问题的。问题是如何从类定义中得到它。
s
scruel

如果您正在使用弹簧:

public static Class<?>[] resolveTypeArguments(Class<?> parentClass, Class<?> subClass) {
    if (subClass.isSynthetic()) {
        return null;
    }
    return GenericTypeResolver.resolveTypeArguments(subClass, parentClass);
}

顺便说一句,对于非子类类,GenericTypeResolver 仍然会像提到的问题一样为 null,因为此类类的通用信息在编译后完全是 erased

解决这个问题的唯一方法可能是:

public class GenericClass<T>
{
    private final Class<T> clazz;
    public Foo(Class<T> clazz) {
        this.clazz= clazz;
    }
    
    public Type getMyType()
    {
        return clazz;
    }
}

S
Shtefan

如果您无法更改泛型类并使用本页已说明的方法之一,那么简单的方法是根据运行时实例类名称获取类型类。

Class getType(GenericType runtimeClassMember){
if (ClassA.class.equals(runtimeClassMember.getClass()){
  return TypeForClassA.class;
} else if (ClassB.class.equals(runtimeClassMember.getClass()){
  return TypeForClassB.class;
} 

//throw an expectation or do whatever you want for the cases not described in the if section.
}

O
Odwori

使用返回类类型的抽象方法,然后在该类中使用它,并且无论您在何处扩展泛型类,都必须实现该抽象方法以返回所需的类类型

public class AbsractService<T>{
  public abstract Class<T> getClassType ();
   .......
}

在运行时

class AnimalService extends AbstractService<MyType>{
    
public Class<MyType> getClassType (){
        return MyType.class;
 }

  .....
}

B
Bonestack

我和上面的@Moesio 做了同样的事情,但在 Kotlin 中可以这样做:

class A<T : SomeClass>() {

    var someClassType : T

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

}

j
jhyry-gcpud

这是受到 Pablo 和 CoolMind 的回答的启发。有时我也使用了 kayz1 的答案中的技术(也表达在许多其他答案中),我相信这是一种体面且可靠的方式来完成 OP 的要求。

我首先选择将其定义为接口(类似于 PJWeisberg),因为我有可以从该功能中受益的现有类型,特别是异构通用联合类型:

public interface IGenericType<T>
{
    Class<T> getGenericTypeParameterType();
}

我在通用匿名接口实现中的简单实现如下所示:

//Passed into the generic value generator function: toStore
//This value name is a field in the enclosing class.
//IUnionTypeValue<T> is a generic interface that extends IGenericType<T>
value = new IUnionTypeValue<T>() {
    ...
    private T storedValue = toStore;
    ...
    
    @SuppressWarnings("unchecked")
    @Override
    public Class<T> getGenericTypeParameterType()
    {
        return (Class<T>) storedValue.getClass();
    }
}

我想这也可以通过使用类定义对象作为源来构建,这只是一个单独的用例。我认为关键是正如许多其他答案所说,以一种或另一种方式,您需要在运行时获取类型信息才能在运行时使用它;对象本身保持它们的类型,但是擦除(也正如其他人所说,使用适当的引用)会导致任何封闭/容器类型丢失该类型信息。


为什么投反对票?给定类型擦除,需要在运行时存储类型以完全保留它,并且在某些高级情况下,应该有一种托管方式来处理运行时需要完整类型信息的情况。我遇到了使用 JNA 提取本机值的情况,其中需要从本机类型指示符和本机空间的字节值手动推断和处理 Java 类型信息。在非常有限的情况下,我需要更多的反对意见才能认为这不是这样的事情的有效用例。
T
TheLetch

它可能对某人有用。您可以使用 java.lang.ref.WeakReference;这边走:

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

  N getType(){
    return variableToGetTypeFrom.get();
  }
}

这个类应该如何使用?为什么是WeakReference?请在您的答案中提供一些解释,而不仅仅是一些代码。
因此,如果您有 SomeClass<MyClass>,您可以实例化 SomeClass 并在该实例上调用 getType,并使运行时为 MyClass
当然可以,但为什么是 WeakReference?您所说的与大多数其他答案没有什么不同。
首先,我的方法更短(代码更少),其次,弱引用不会阻止它们的引用被最终化,据我所知,它不使用反射,因此速度很快
这不会得到任何东西的类型,它会返回该类型的对象,仅供参考,您可以使用任何类型的包装器(AtomicReferenceListSet)来执行此操作。
M
Mark Karchner

我发现这是一个简单易懂且易于解释的解决方案

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

解释(T...t)。 (这就是为什么这段代码不起作用。)