如何从类对象获取(泛型)类型

时间:2015-08-05 11:54:31

标签: java templates generics

我想这样做:

Get "Class" object from generic type T

但反过来说。所以我想获得类对象的类型。在Pseudocode中我想做这样的事情:

public class ExampleClass {

    public static final Class<?>[] classes = new Class[]{MyClass1.class,
    MyClass2.class, MyClass3.class, MyClass4.class, MyClass5.class};

    public void myMethod() {
        for (Class<?> c : DataBaseConfigUtils.classes ) {
            MyObjectDAO<c.getType(), Integer> myObjectDAO = getMyObjectDAO(c);
            ArrayList<c.getType()> list = myObjectDAO.queryAll();
            for (c.getType() element : list) {
                processElement(element);
            }

        }
    }

    public <T> MyObjectDAO<T, Integer> getMyObjectDAO(Class<T> c) {
        return doSomething(c);
    }
}

但没有像Class.getType()那样的东西。那么如何获取类对象的类型呢?

3 个答案:

答案 0 :(得分:4)

这在Java中是不可能的。该类型仅在编译期间使用并被擦除。此信息不存在于字节码或运行时环境中。

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

  

泛型被引入Java语言以提供更紧密的类型   在编译时检查并支持通用编程。至   实现泛型,Java编译器将类型擦除应用于:

     
      
  • 如果类型参数是无界的,则将泛型类型中的所有类型参数替换为其边界或对象。生成的字节码,   因此,只包含普通的类,接口和方法。
  •   
  • 如有必要,插入类型转换以保护类型安全。
  •   
  • 生成桥接方法以保留扩展泛型类型中的多态性。
  •   
     

类型擦除确保不会为参数化创建新类   类型;因此,泛型不会产生运行时开销。

答案 1 :(得分:1)

您的通用方法getMyObjectDAO似乎回答了您自己的问题。如果在类型中有一个带通配符的变量,则只要需要引用代码中的类型,就可以使用通用的辅助方法。您可以将代码重构为:

public class ExampleClass {

    public static final Class<?>[] classes = new Class[]{MyClass1.class,
        MyClass2.class, MyClass3.class, MyClass4.class, MyClass5.class};

    public void myMethod() {
        for (Class<?> c : DataBaseConfigUtils.classes ) {
            act(c);    
        }
    }

    private <T> void act(Class<T> c) {
        MyObjectDAO<T, Integer> myObjectDAO = getMyObjectDAO(c);
        ArrayList<T> list = myObjectDAO.queryAll();
        for (T element : list) {
            processElement(element);
        }
    }

    public <T> MyObjectDAO<T, Integer> getMyObjectDAO(Class<T> c) {
        return doSomething(c);
    }
}

重要的是要意识到这一点的局限性。假设您有一个List<?>类型的变量。虽然 可以使用私有通用帮助器方法,使您可以将其视为List<T>并在代码中使用类型T,但键入擦除意味着您可以& #39; t在运行时查询类型T。所以以下所有都是非法的

if (T == String) { //do something; }

if (T.class == String.class) { //do something }

if (a instanceof T) { //do something }

通常的解决方法是将辅助方法的一个参数设为Class<T>对象,因为可以执行

if (clazz == String.class) { //do something }

(请注意,如果T本身是泛型类型,则无效。因为您无法编写List<String>.class,例如。有一种称为超类型令牌的解决方法。)

由于您使用的对象是 Class个对象,因此类型擦除不应该​​导致任何问题。

答案 2 :(得分:0)

例如:

ParameterizedType superclass = (ParameterizedType)getClass().getGenericSuperclass();
    this.entityClass = (Class<T>) superclass.getActualTypeArguments()[0];

但是如果你想添加Generic类型,我想你有设计错误。最佳实践 - 您应该创建父(抽象)类或接口并在您的代码中使用它:

public abstract class ABaseEntity{
...
}
public class Child extends ABaseEntity{
...
}

使用您的BaseEntity创建基础dao将返回一些实体扩展您的ABaseEntity:

public class AbstarctDAO<T extends ABaseEntity, PK extends Serializable>  {

@PersistenceContext
protected EntityManager em;
protected Class<T> entityClass;

protected AbstarctDAO() {

}
@PostConstruct
public void init(){
    final ParameterizedType superclass = (ParameterizedType)getClass().getGenericSuperclass();
    this.entityClass = (Class<T>) superclass.getActualTypeArguments()[0];

}
    @Override
    public T create(T object) throws Exception {
        em.persist(object);
        em.flush();
        return object;
    }
...
}

例外情况: 1。

 AbstarctDAO dao = new AbstarctDAO();
        try {
            dao.create(new Child ());
        }catch (Exception e){

        }
 2.
@ Stateless
@ Transactional
public class ChildDAO extends AbstarctDAO<Child , Long> {
    @Transactional(Transactional.TxType.SUPPORTS)
    public Child getChildByName(String name) {
        ...
        return (Child)query.getSingleResult();
    }

}