如何调用具有通用类型作为参数的映射的函数

发布于 2024-09-10 15:57:26 字数 2601 浏览 2 评论 0原文

我正在使用 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] = 
{
  ...
}

好的,所以该函数应该可以工作...但是我该怎么称呼它呢?举例来说,我想使用这样的 Map 进行查询:

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]

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

I'm using Scala to do typesafe JPA2 Criteria Queries.
Therefore i have a Java MetaModel Class (the only Java in my code, the rest is Scala -> pure Scala problem), which holds my model attributes:

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

To do a query for one single attribute, i have this function:

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

Which i can call like this:

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

Now i'm trying to create a query function, with which i can query for multiple attributes at once, and therefore i want to use a Map of SingularAttributes as a parameter for my function:

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

Ok, so the function should work... But how can i call it??? Say for example i want to query with a Map like this:

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

So my first approach of defining this Map in Scala and passing it to findByAttributes is this:

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

Unfortunately, the compiler isn't satisfied when passing searchFor to the findByAttributes-function, producing the error below:

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]

That's the most complicated generic problem i ever had. A bit too high for my skill ;) Anyone knows how i can build the right map type that i can pass to the function? Is it even possible, or can't the compiler infer the type anymore in my case? Or am i using the wrong datastructure?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

前事休说 2024-09-17 15:57:26

当您按如下方式声明映射时,

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 元组,依此类推...)。这有点乏味,但这是我现在能想到的最好的解决方案。

When you declare the map as follows

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

the compiler will infer a somewhat weird type:

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

Try it out yourself in Scala's REPL!

The problem here is that the compiler infers Any as the common type of String and Int which is actually true. So, you're losing any information about the actual values in the map. And of course the key also isn't really what you would want.

This means that Map is obviously not the right type. You could try to use a n-tuple instead:

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

So, all type information will be stored correctly. Of course, you will then have to create several findByAttributes functions (one for 1-tuple, one for 2-tuple, 3-tuple and so on ...). This is a bit tedious, but it's the best solution I could think of now.

野生奥特曼 2024-09-17 15:57:26

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

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)

Edit: 从我的摆弄来看,可能需要 findByAttributeValuePair 方法来调用 findByAttribute 进行类型检查在 Scala 2.8 REPL 中。

I haven't fully worked out the details, but I think something like

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] = 
{
   ...
}

might work. Then a call would look like

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

Edit: The findByAttributeValuePair method may be necessary to get the call to findByAttribute to type-check, judging from my fiddling in the Scala 2.8 REPL.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文