如何调用具有Generic Type的Map作为参数的函数

时间:2010-07-19 18:32:58

标签: scala scala-2.8

我正在使用Scala来执行类型安全的JPA2 Criteria Queries。 因此,我有一个Java MetaModel类(我的代码中唯一的Java,其余的是Scala - >纯Scala问题),它拥有我的模型属性:

@StaticMetamodel(User.class)
public class User_ {
  public static volatile SingularAttribute<User, Long> id;
  public static volatile SingularAttribute<User, String> name;
}

要对一个属性进行查询,我有这个功能:

def findByAttribute[T](
  attribute:SingularAttribute[User, T], value:T):ArrayList[User] = 
{
  ...
}

我可以这样打电话:

userEJB.findByAttribute(User_.name, "John")

现在我正在尝试创建一个查询函数,我可以使用它查询多个属性,因此我想使用SingularAttributes的Map作为我的函数的参数:

// Map is of type scala.collection.immutable.Map
def findByAttributes[T](
  attributes:Map[SingularAttribute[User, T], T]):ArrayList[User] = 
{
  ...
}

好的,所以这个功能应该有效...但我怎么称它呢???比方说,我想用这样的地图查询:

User_.name -> "Doe"
User_.id -> 5

所以我在Scala中定义这个Map并将它传递给findByAttributes的第一种方法是:

val criteria = Map(User_.name -> "Doe", User_.id -> 5)
// Produces Compiler Error
val users = userEJB.findByAttributes(criteria)

不幸的是,在将searchFor传递给findByAttributes函数时,编译器不满意,产生以下错误:

no type parameters for method findByAttributes: (attributes: 
Map[javax.persistence.metamodel.SingularAttribute[net.teachernews.model.User,
T],T])
java.util.ArrayList[net.teachernews.model.User] exist so that it can be applied to 
arguments (scala.collection.immutable.Map[javax.persistence.metamodel.
SingularAttribute[
net.teachernews.model.User, _ 
>: java.lang.Long with java.lang.String 
<: java.lang.Comparable[_ 
>: java.lang.Long with java.lang.String 
<: java.lang.Comparable[_ >: java.lang.Long with java.lang.String 
<: java.io.Serializable] with java.io.Serializable] 
with java.io.Serializable],Any])  --- because --- 
argument expression's type is not compatible with formal parameter type;  
found   : 
scala.collection.immutable.Map[javax.persistence.metamodel.SingularAttribute[
net.teachernews.model.User, _ 
>: java.lang.Long with java.lang.String 
<: java.lang.Comparable[_ 
>: java.lang.Long with java.lang.String    
<: java.lang.Comparable[_ >: java.lang.Long with java.lang.String 
<: java.io.Serializable] with java.io.Serializable] with java.io.Serializable],
Any]  
required: Map[javax.persistence.metamodel.SingularAttribute[
net.teachernews.model.User,?T],?T]

这是我曾经遇到的最复杂的一般问题。对我的技能来说有点太高了;)任何人都知道如何构建我可以传递给函数的正确的地图类型?它甚至可能,或者编译器不能在我的情况下推断出类型?或者我使用错误的数据结构?

2 个答案:

答案 0 :(得分:2)

声明地图时如下

val criteria = Map(User_.name -> "Doe", User_.id -> 5)

编译器将推断出一种有点奇怪的类型:

scala.collection.immutable.Map[SingularAttribute[User, _ >: Long with String], Any]

在Scala的REPL中自己尝试一下!

这里的问题是编译器将Any推断为StringInt的常见类型,这实际上是正确的。因此,您丢失了有关地图中实际值的任何信息。当然,关键也不是你想要的。

这意味着Map显然不是正确的类型。您可以尝试使用n元组:

((User_.name -> "Doe"), (User_.id -> 5))

因此,所有类型信息都将被正确存储。当然,您将需要创建多个findByAttributes函数(一个用于1元组,一个用于2元组,3元组等等)。这有点单调乏味,但这是我现在想到的最好的解决方案。

答案 1 :(得分:1)

我还没有完全弄清楚细节,但我想像是

type AttributeValuePair[T] = Pair[SingularAttribute[User, T], T]

def findByAttributeValuePair[T](p:AttributeValuePair[T]):ArrayList[User] =
   findByAttribute(p._1, p._2)

def findByAttributes(attributes:AttributeValuePair[_]*):ArrayList[User] = 
{
   ...
}

可能会奏效。然后一个电话看起来像

findByAttributes(User_.name -> "Doe", User_.id -> 5)

编辑findByAttributeValuePair方法可能需要调用findByAttribute来进行类型检查,从我在Scala 2.8 REPL中摆弄来判断。