我正在尝试为我的应用程序编写通用数据访问层。我有多个休眠实体,它们基本上是相同的,并在Java中表示为类层次结构(在Hibernate中未实现为层次结构):

public abstract class Entity1 {
    // some implementation
}
public class Entity2 extends Entity1 {
    // some implementation
}
public class Entity3 extends Entity1 {
    // some implementation
}


这些实体的DAO大部分相同(方法的类型签名和要求休眠的类除外)。我希望能够像这样编写通用DAO:

public interface EntityDao<T extends Entity1> {
    List<T>getEntities();
    void saveOrUpdateEntity(T entity);
}

public class EntityDaoImpl implements EntityDao<Entity1> {
    private final HibernateTemplate hibernateTemplate;

    private final Class<DBEnumerationDto> clazz;

    public DBEnumerationDaoHibernateImpl(SessionFactory sessionFactory, Class<DBEnumerationDto> clazz) {
        this.hibernateTemplate = new HibernateTemplate(sessionFactory);
        this.clazz = clazz;
    }

    @Override
    public List<Entity1> getEntities() {
        return this.hibernateTemplate.loadAll(this.clazz);
    }
    @Override
    public void saveOrUpdateEntity(Entity1 entity) {
        this.hibernateTemplate.saveOrUpdate(entity);
    }
}


到目前为止,一切都很好。但是使用此方法时会出现问题:

Entity1 test = new Entity1();
Entity1Dao<? extends Entity1> dao = ...; // get the dao forthe current operation
dao.saveOrUpdate(test);


这给出了编译器错误:The method saveOrUpdateEntity(capture#5-of ? extends Entity1) in the type EntityDao<capture#5-of ? extends Entity1> is not applicable for the arguments (Entity1)

我想这个问题与Java Generics: casting to ? (or a way to use an arbitrary Foo<?>)有关,但我无法真正掌握哪种方式。

我应该如何修正我的代码?还是我的方法不对?

最佳答案

请仔细考虑一下失败示例第二行中的问号(通配符)是什么意思。

您已经获得了Entity1Dao,具有未知的通用参数(唯一已知的是该参数是Entity1的子类)。因此,实际上按以下方式实施将是完全合法的:

Entity1Dao<? extends Entity1> dao = getEntityDao();

private Entity1Dao<Entity2> getEntityDao()
{
    return new Entity1Dao<Entity2>(); // Or whatever (the construction itself is irrelevant)
}


由于有通配符,因此分配Entity1Dao<Entity2>是完全合法的。现在,您进入下一行并尝试调用dao.saveOrUpdate(),并传入类型为Entity1的对象。

这是行不通的-正如我们上面显示的那样,dao是在Entity2上参数化的,因此只有具体方法saveOrUpdate(Entity2 entity)!因此,为什么编译器会给您输入警告。



总之,您的问题是“ extends”关键字,该关键字允许通配符参数成为您实际类型的子类,因此无法处理它。如果将通配符更改为使用超级(即<? super Entity1>),则会进行编译,因为编译器可以确保无论实际的通用类型如何,saveOrUpdate方法都将接受Entity1类型的参数。

通常,与某些确切类型相比,您需要相同的参数既要具有super参数又要具有扩展参数,这意味着您完全不能使用通配符。在这种情况下,您可能希望根据类的具体类型来简化整个方法,如下所示:

public <T extends Entity1> void saveEntityExample(T test)
{
   Entity1Dao<T> dao = getEntityDao();
   dao.saveOrUpdate(test);
}

private <T extends Entity1> Entity1Dao<T> getEntityDao()
{
   // Get the DAO however
}




您可能还想查看this answer中链接的Josh Bloch的演讲,特别是要了解“ PECS”的概念。与始终以“扩展”为答案的类不同,在泛型方面,人们需要考虑它们是“扩展”还是“超级”,而助记符是记住通用规则的一种有用方法。

关于java - 泛型和Java:“扩展”如何工作?,我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/2229050/

10-13 09:08