【问题标题】:How to determine the class of a generic type?如何确定泛型类型的类?
【发布时间】:2023-03-26 19:27:01
【问题描述】:

我正在创建一个泛型类,并且在其中一种方法中我需要知道当前使用的泛型类型的类。原因是我调用的方法之一期望 this 作为参数。

例子:

public class MyGenericClass<T> {
  public void doSomething() {
    // Snip...
    // Call to a 3rd party lib
    T bean = (T)someObject.create(T.class);
    // Snip...
  }
}

显然,上面的示例不起作用并导致以下错误:类型参数 T 的非法类文字。

我的问题是:有人知道一个好的替代方案或解决方法吗?

【问题讨论】:

    标签: java generics


    【解决方案1】:

    还是同样的问题:通用信息在运行时被删除,无法恢复。一种解决方法是在静态方法的参数中传递类 T:

    public class MyGenericClass<T> {
    
        private final Class<T> clazz;
    
        public static <U> MyGenericClass<U> createMyGeneric(Class<U> clazz) {
            return new MyGenericClass<U>(clazz);
        }
    
        protected MyGenericClass(Class<T> clazz) {
            this.clazz = clazz;
        }
    
        public void doSomething() {
            T instance = clazz.newInstance();
        }
    }
    

    它很丑,但它有效。

    【讨论】:

    • 这确实很丑,但是使用受保护构造函数的技巧对我来说已经足够了。我将泛型类用作抽象类,用作 5 到 10 个具体类的基础。谢谢!
    • 是否可以使用它来确定对象是什么泛型类型?例如if (obj.getClazz() is a String) doThis(); if (obj.getClazz() is a Integer) doThat();
    • ^解决方案:if(obj.getClazz().equals(String.class))...
    • @dwjohnston 假设您已经拥有该类的实例。
    • +1 我只是调用我的工厂方法of。将MyGenericClass.of(String.class)new MyGenericClass&lt;String&gt;() {} 进行比较。反射方法并不能节省任何东西。
    【解决方案2】:

    我只是指出了这个解决方案:

    import java.lang.reflect.ParameterizedType;
    
    public abstract class A<B> {
        public Class<B> g() throws Exception {
            ParameterizedType superclass =
                (ParameterizedType) getClass().getGenericSuperclass();
    
            return (Class<B>) superclass.getActualTypeArguments()[0];
        }
    }
    

    如果A 由子类赋予具体类型,则此方法有效:

    new A<String>() {}.g() // this will work
    
    class B extends A<String> {}
    new B().g() // this will work
    
    class C<T> extends A<T> {}
    new C<String>().g() // this will NOT work
    

    【讨论】:

    • 不错的解决方案。尚未确认它是否有效,但它会导致更清晰的继承。
    • 我刚刚尝试过它并且它有效....向你致敬@Chrisoph!这特别好,因为它也可以在不更改派生类的情况下工作!
    • 对我来说就像一个魅力,我不必弄乱继承的类。 +1!
    • 它运行良好,但我在“(Class) superclass.getActualTypeArguments()[0];”附近收到“类型安全:未经检查的从 Type 到 Class 的强制转换”警告.怎么删除?
    • 一个问题;为什么方法g会抛出异常?
    【解决方案3】:

    不幸的是,Christoph 所写的解决方案仅在非常有限的情况下有效。 [编辑:正如下面评论的那样,我不再记得我对这句话的推理,它可能是错误的:“请注意,这首先只适用于抽象类。”] 下一个困难是 g() 仅适用于 DIRECT A 的子类。不过,我们可以解决这个问题:

    private Class<?> extractClassFromType(Type t) throws ClassCastException {
        if (t instanceof Class<?>) {
            return (Class<?>)t;
        }
        return (Class<?>)((ParameterizedType)t).getRawType();
    }
    
    public Class<B> g() throws ClassCastException {
        Class<?> superClass = getClass(); // initial value
        Type superType;
        do {
            superType = superClass.getGenericSuperclass();
            superClass = extractClassFromType(superType);
        } while (! (superClass.equals(A.class)));
    
        Type actualArg = ((ParameterizedType)superType).getActualTypeArguments()[0];
        return (Class<B>)extractClassFromType(actualArg);
    }
    

    这在实践中适用于许多情况,但并非总是如此。考虑:

    public class Foo<U,T extends Collection<?>> extends A<T> {}
    
    (new Foo<String,List<Object>>() {}).g();
    

    这将抛出一个ClassCastException,因为这里的类型参数根本不是ClassParameterizedType;这是TypeVariableT。所以现在你会被困在试图弄清楚 T 应该代表什么类型,等等。

    我认为唯一合理、通用的答案类似于 Nicolas 最初的答案——一般来说,如果您的类需要实例化在编译时未知的其他类的对象,则您的类的用户需要通过类文字(或者,也许是工厂)显式地添加到您的类中,而不仅仅依赖于泛型。

    【讨论】:

    • Christoph 的解决方案确实适用于非抽象类,我已经对其进行了测试,并将我的工作包含在此处的答案中。
    • @ChrisNash 你可能是对的——我写这篇文章已经快 4 年了,我不再写太多 Java,我并不清楚我的意思是什么“抽象类”评论。我仍然坚持尼古拉斯和我的回答的普遍正确性。正如我所说,我想可能有可能写出一个一路“走下坡路”的方法,但我不确定我是否想要....跨度>
    【解决方案4】:

    我找到了另一种方法来获取泛型对象的类

    public Class<?> getGenericClass(){
             Class<?> result =null;
             Type type =this.getClass().getGenericSuperclass();
    
             if(type instanceofParameterizedType){
                  ParameterizedType pt =(ParameterizedType) type;
                  Type[] fieldArgTypes = pt.getActualTypeArguments();
                  result =(Class<?>) fieldArgTypes[0];
            }
            return result;
      }
    

    【讨论】:

    • 现在我知道为什么会这样了,这只适用于需要定义实现的抽象类,甚至比你需要在实现中定义 T 类型并且不能允许设置新类使用过程中的价值。它在某些情况下不起作用
    【解决方案5】:

    我会详细说明 Christoph 的解决方案。

    这是 ClassGetter 抽象类:

    private abstract class ClassGetter<T> {
        public final Class<T> get() {
            final ParameterizedType superclass = (ParameterizedType)
                getClass().getGenericSuperclass();
            return (Class<T>)superclass.getActualTypeArguments()[0];
        }
    }
    

    这是一个静态方法,它使用上面的类来查找泛型类的类型:

    public static <T> Class<T> getGenericClass() {
        return new ClassGetter<T>() {}.get();
    }
    

    作为它的用法示例,您可以使用以下方法:

    public static final <T> T instantiate() {
        final Class<T> clazz = getGenericClass();
        try {
            return clazz.getConstructor((Class[])null).newInstance(null);
        } catch (Exception e) {
            return null;
        }
    }
    

    然后像这样使用它:

    T var = instantiate();
    

    【讨论】:

    • 此代码不起作用。它抛出java.lang.ClassCastException: sun.reflect.generics.reflectiveObjects.TypeVariableImpl cannot be cast to java.lang.Class
    • 您使用的泛型T 的类型是什么?
    【解决方案6】:

    使用TypeTools 可以很容易地解决T:

    Class<T> t = (Class<T>) TypeResolver.resolveRawArguments(
                                    MyGenericClass.class, getClass());
    

    【讨论】:

    • 这无法编译,因为“MyGenericClass.class 出错...不能从类型变量中获取类文字。”
    【解决方案7】:

    公共类 DatabaseAccessUtil {

    EntityManagerFactory entitymanagerfactory;
    EntityManager entitymanager;
    
    public DatabaseAccessUtil() {
        entitymanagerfactory=Persistence.createEntityManagerFactory("bookmyshow");
        entitymanager=entitymanagerfactory.createEntityManager();
    }
    
    public void save  (T t) {
        entitymanager.getTransaction().begin();
        entitymanager.persist(t);
        entitymanager.getTransaction().commit();
    }
    
    public void update(T t) {
        entitymanager.getTransaction().begin();
        entitymanager.persist(t);
        entitymanager.getTransaction().commit();
    }
    
    public void delete(T t) {
        entitymanager.getTransaction().begin();
        entitymanager.remove(t);
        entitymanager.getTransaction().commit();
    }
    
    public Object retrieve(Query query) {
        return query.getSingleResult();
    }
    //call the method - retrieve(object,requiredclass.class)
    public Object retrieve(Object primaryKey,class clazz) throws Exception {
    
        return entitymanager.find(clazz,primaryKey);    
    
    }
    

    }

    【讨论】:

    • 请正确格式化您的代码块,并在代码中包含public class DatabaseAccessUtil { 和结尾}。此外,如果您添加一些措辞而不是仅添加代码会更好。
    • 这与问题几乎没有关系。这不是问题的解决方案,它涉及到JPA,这甚至不会出现在问题中。
    猜你喜欢
    • 2022-12-16
    • 2011-02-24
    • 2019-11-04
    • 2011-05-24
    • 2019-07-21
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多