我有以下功能:
/**
* Finds all entities of a certain type
* @param <T> The type of the entity
* @param entityType The class of the entity
* @return A list of all the entities found, null if the entity is not in the database
* or on error
*/
public <T> List<T> findAll(Class entityType)
{
javax.persistence.criteria.CriteriaQuery cq = getEntityManager().getCriteriaBuilder().createQuery();
cq.select(cq.from(entityType));
return getEntityManager().createQuery(cq).getResultList();
}
你会注意到它是相当重复的。无论如何,我可以重构此函数,以便它不需要将Class作为参数。反正我是否可以使用传入的泛型类型?
答案 0 :(得分:2)
不,你不能 - 直接。在几段中,我将向您展示解决问题的另一种方法。
Java泛型是通过 type erasure 实现的,这意味着所有类型信息都会在运行时被删除。当你的方法被调用时,它知道它应该返回一个List
,但是在运行时没有什么可以告诉它它应该返回List<Foo>
。 ArrayList
构造函数不需要访问类对象来完成其工作;但是,你的代码确实如此。
解决这个问题的方法是只是传递类,因为这是一个泛型方法(而不是泛型类),你可以这样做。如果您将方法的声明更改为
public <T> List<T> findAll(Class<T> entityType)
然后你可以用课程来调用它:
findAll(String.class)
并且编译器将自动检测到它应该返回List<String>
。它减少了冗余,但是通过从类中推断出类型参数而不是相反。这是解决此类问题的标准方法 - 它在Guava等库中显示出很多。
答案 1 :(得分:1)
创建“通用DAO”的标准做法是拥有一个可参数化的抽象类,并具有带特定参数的子类。这样,方法本身已经使用正确的类型进行参数化。
看看这个例子:
答案 2 :(得分:1)
不是,不。泛型是一个编译时功能。在运行时,API的调用者可以为entityType提供Class<T>
的任何实例,因此您需要在运行时将其提供给hibernate。编译器无法为每个可能的T
基本构建单独的方法版本,这是为了省略类参数所必须做的。
此外,Class
是一种原始类型,您已经使用了不正确的泛型;)
答案 3 :(得分:1)
Java使用Type erasure,这意味着泛型类型参数在运行时不可用(在编译时用于确保代码对于类型是正确的)。这意味着要使用持久性,您需要显式传递该类,以便运行时可以弄清楚如何执行持久性(例如,持久对象所属的类)
答案 4 :(得分:1)
简单的答案是“不”。由于称为类型擦除的东西,所有参数化类型在运行时在编译的字节码中被视为Object.class
。泛型类型仅在编译时使用,以防止您使用错误。
答案 5 :(得分:0)
您可以或多或少地在Scala中执行您所要求的操作。 (我不确定你是否可以使用scala,但仅供参考,这里是。)
object Main extends App{
def findAll[T : Manifest]() : Array[T] = {
var a = new Array[T](0)
println(a.getClass)
a
}
var result = findAll[String]()
println(result.getClass)
var result2 = findAll[Array[Int]]()
println(result2.getClass)
}
通过使用隐式Manfiest,scala编译器会记录在编译时擦除泛型的内容。