【问题标题】:How to call a function that has a Map with a Generic Type as a parameter如何调用具有泛型类型作为参数的 Map 的函数
【发布时间】:2010-07-19 18:32:57
【问题描述】:

我正在使用 Scala 来执行类型安全的 JPA2 标准查询。 因此我有一个 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]

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

【问题讨论】:

  • 使用以下划线结尾的标识符是您可能做出的最糟糕的选择之一。
  • 在为 JPA2 定义元模型时,使用以下划线字符结尾的标识符是常见的做法,至少对于 Java 来说是这样。请参阅java.sun.com/developer/technicalArticles/JavaEE/…(搜索 Employee_)。
  • 如果在调用 findByAttributes 时显式指定类型 Map[SingularAttribute[User, String], String] 和类型参数 [String] 会发生什么?
  • 可能是这样,但Person_&lt;% 在 Java 中不是有效标识符,但在 Scala 中是有效的。这意味着像[Person_&lt;%Ordered[Person_]] 这样的语句与[Person_ &lt;% Ordered[Person_]] 的含义不同。

标签: scala scala-2.8


【解决方案1】:

当你如下声明地图时

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-tuple,一个用于 2-tuple,3-tuple 等等......)。这有点乏味,但这是我现在能想到的最好的解决方案。

【讨论】:

  • 不幸的是,参数必须是可变长度的,因为我想为我的所有实体使用这个函数,而不必修改它。并且用户不必注意参数传递的顺序。没有另一种类型作为选择吗?感谢您的提示,帮助我理解了问题。
【解决方案2】:

我还没有完全弄清楚细节,但我认为类似

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)

编辑:从我对 Scala 2.8 REPL 的摆弄来看,findByAttributeValuePair 方法可能是调用 findByAttribute 进行类型检查所必需的。

【讨论】:

  • 哇。我知道它必须以某种方式成为可能!刚刚测试了一下,好像还可以。类型被正确检查。你知道像我的 findByAttributes 这样带有所有类型检查的函数在 Java 中是否可行?将是一个很好的支持 Scala 的论点...
  • 在这种情况下,Java 版本不会有太大的不同。它只会更冗长,而且您必须找到或编写一个 Pair 类,因为它不包含在 Java 库中。调用必须有点丑陋,使用工厂方法来执行类型检查并构造对:findByAttributes(pair(User_.name, "Doe"), pair(User_.id, 5))
猜你喜欢
  • 2010-10-04
  • 2019-03-05
  • 2022-01-04
  • 2018-02-18
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2014-03-11
相关资源
最近更新 更多