有 Java 编程相关的问题?

你可以在下面搜索框中键入要查询的问题!

java我能做这个通用的事情吗?

我似乎在Java泛型方面遗漏了一些东西,因为我认为有些东西很简单,但在我看来这是不可能做到的。也许你能帮我

这是一个场景:我正在用简单的CRUD操作编写一个通用抽象DAO,这样我的应用程序的每个特定DAO都可以免费使用它:

public abstract DefaultDAO<T,V> {
  private EntityManager manager;

  public BaseDAO(EntityManager em) {
    this.manager = em;
  }

  public void create(T entity) {
    manager.persist(entity);
  }

  // You know the others...

  public T read(V pk) {
    // Now, here is the problem. 
    // EntityManager signature is: public <T> T find(Class<T> entityClass, Object primaryKey);
    // So I must provide the type of the object this method will be returning and 
    // the primary key.
    // resulting object will be T typed and pk type is V type (although is not needed to type it since the method expects an Object)
    // So... I expected to be allowed to do something like this
    return manager.find(T, pk); // But it's not allowed to use T here. T is not an instance
  }   
}

现在我要去实现一个特定的DAO:

public PersonDAO extends DefaultDAO<PersonEntity, Long> {
  public PersonDAO(EntityManager em) {
    super(em);
  }
  // CRUD methods are inherited
}

我的DAO的客户端代码是:

EntityManager manager = ...
PersonDAO dao = new PersonDAO(manager);
Long pk = .....
PersonEntity person = dao.find(pk); // DAO would return a PersonEntity

当客户机执行代码时,BaseDAO知道它必须返回的实体的类型和该实体的主键的类型,因为我在特定的dao上设置了它,但我不知道如何正确地编写read()方法

希望你能帮忙。非常感谢


共 (5) 个答案

  1. # 1 楼答案

    只要类在泛型方面遵循一致的类层次结构(即基类和具体类之间的继承层次结构中的任何中间类以相同的顺序使用相同的泛型参数),就可以使用反射来实现这一点

    我们在抽象类中使用类似的东西,定义为HibernateDAO,其中T是实体类型,K是PK:

    private Class getBeanClass() {
        Type daoType = getClass().getGenericSuperclass();
        Type[] params = ((ParameterizedType) daoType).getActualTypeArguments();
        return (Class) params[0];
    }
    

    它闻起来有点异味,但却能抵消路过的恶心。从构造器中的具体实现初始化,以便坚持以这种方式保持类型层次结构的一致性

  2. # 3 楼答案

    您试图将类型参数当作普通表达式来使用。你不能这样做。在其他语言中,您可能可以在执行时获取类型参数的类,但在Java中,由于类型擦除,您无法获取

    在这种情况下,您需要在执行时传入Class<T>,例如传递给构造函数:

    public abstract class DefaultDAO<T, V> {
    
        private EntityManager manager;
        private final Class<T> clazz;
    
        public DefaultDAO(EntityManager em, Class<T> clazz) {
            this.manager = em;
            this.clazz = clazz;
        }
    
        public T read(V pk) {
            return manager.find(clazz, pk);
        }
    }
    
  3. # 4 楼答案

    Java在运行时不再具有泛型类信息(称为类型擦除)。我所做的是给我的抽象DAO提供泛型类的实例

    public abstract DefaultDAO<T,V> {
    
      protected Class<T> genericClass;
      private EntityManager manager;
    
      protected BaseDAO(EntityManager em, Class<T> implclass) {
        this.manager = em;
      }
    
      public void create(T entity) {
        manager.persist(entity);
      }
    
      // You know the others...
    
      public T read(V pk) {
        return manager.find(this.getGenericClass(), pk); // But it's not allowed to use T here. T is not an instance
      }
    
      public Class<T> getGenericClass()
      {
        return genericClass;
      }
    }
    
    public class Blubb
    {
        private String id;
    
        // Getters and Stuff...
    }
    
    public class BlubbDao extends DefaultDAO<Blubb, String>
    {
        public BlubbDao(EntityManager em)
        {
            super(em, Blubb.class);
        }
    }
    

    我不能保证它会用完,但我希望你能明白

  4. # 5 楼答案

    我认为你可以做到:

     public <T> T find(Class<T> clazz, Object id) {
            return entityManager.find(clazz, id);
        }