我正在尝试为我的应用程序编写通用数据访问层。我有多个休眠实体,它们基本上是相同的,并在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/