Как вызвать функцию, имеющую в качестве параметра карту с универсальным типом - PullRequest
1 голос
/ 19 июля 2010

Я использую Scala для выполнения безопасных запросов критериев JPA2.Поэтому у меня есть класс Java MetaModel (единственная Java в моем коде, остальное - проблема Scala -> pure 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 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 и передачи ее в 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 ]

2 голосов
/ 20 июля 2010

Когда вы объявляете карту следующим образом

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

компилятор выведет несколько странный тип:

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

Попробуйте сами в REPL Scala!

Проблема здесь в том, что компилятор выводит Any как общий тип String и Int, что на самом деле верно.Таким образом, вы теряете любую информацию о фактических значениях на карте.И, конечно же, ключ не совсем то, что вы хотели бы.

Это означает, что Map, очевидно, не тот тип.Вместо этого вы можете попробовать использовать n-кортеж:

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

Таким образом, вся информация о типах будет храниться правильно.Конечно, вам нужно будет создать несколько findByAttributes функций (одна для 1-кортежа, одна для 2-кортежа, 3-кортежа и т. Д.).Это немного утомительно, но это лучшее решение, которое я могу придумать сейчас.

1 голос
/ 20 июля 2010

Я не до конца проработал детали, но думаю, что-то вроде

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.

...