【问题标题】:Can I do this Generic thing?我可以做这个通用的事情吗?
【发布时间】:2010-12-17 22:23:38
【问题描述】:

似乎我在 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() 方法正确。

希望您能提供帮助。非常感谢!

【问题讨论】:

    标签: java generics dao


    【解决方案1】:

    您正在尝试像使用普通表达式一样使用类型参数。你不能这样做。在其他语言中,您可能能够在执行时获取类型参数的类,但由于 类型擦除,您不能在 Java 中。

    在这种情况下,您需要在执行时传递 Class&lt;T&gt; - 例如给构造函数:

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

    【讨论】:

    • 如果 Java 要支持所谓的物化泛型,这是可能的。然后你可以在这里使用T.class
    • @BalusC:我要等到确认后才开始在答案中讨论它。 Java 泛型在没有将未来可能的功能融入其中的情况下已经足够令人困惑了 :)
    • 给像'clazz'这样的变量命名不是很奇怪吗?这是从哪里来的?
    • @Mykola:你不能称它为“类”,因为那是一个关键字。这是常用的替代方法。
    • 这是 Sun 自己发起的约定 - 如果您查看在 JDK 文档中采用 Class 类型参数的方法(例如 Class 本身的参数),您会发现它们使用 @ 987654326@ 用于参数名称。
    【解决方案2】:

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

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

    我不能保证它会开箱即用,但我希望你明白这一点。

    【讨论】:

      【解决方案3】:

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

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

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

      这有点味道,但权衡了将 .class 从构造函数中的具体实现向上传递的讨厌,以坚持以这种方式保持类型层次结构一致的讨厌。

      【讨论】:

        【解决方案4】:

        我认为你可以这样做:

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

        【讨论】:

          【解决方案5】:

          【讨论】:

            猜你喜欢
            • 2017-02-09
            • 1970-01-01
            • 2013-06-26
            • 1970-01-01
            • 1970-01-01
            • 2012-04-23
            • 1970-01-01
            • 1970-01-01
            • 2018-07-02
            相关资源
            最近更新 更多