Как я могу добавить дополнительное поведение только в списки определенного типа? - PullRequest
3 голосов
/ 17 июня 2011

У меня есть небольшое приложение Scala / Neo4j, которое связывает людей и темы через отношения между «опытным» и «заинтересованным». У него есть REST / Json Api (использующий Scalatra), и я столкнулся с типичной проблемой стирания типов, когда я хотел добавить метод asJson в List [Person] и List [Topic]. Я хотел бы реализовать различное поведение сериализации Json для разных типов контента, но, конечно, типы стираются. Лучшее, что я смог придумать, это следующий трюк во время выполнения:

implicit def topicsOrPeopleAsJson[T](list: List[T]) = new {
    def asJson: String = {
        list match {
            case head :: tail if (head.isInstanceOf[Topic]) => topicsToJson(list.asInstanceOf[List[Topic]])
            case head :: tail if (head.isInstanceOf[Person]) => peopleToJson(list.asInstanceOf[List[Person]])
            case _ => "[]"
        }
    }

    private def peopleToJson(people: List[Person]) = {
        ...
    }

    private def topicsToJson(topics: List[Topic]) = {
        ...
    }
}

Это прекрасно работает, но мне было интересно, есть ли лучшее решение, может быть, что-то, включающее классы типов, тему, с которой я не очень хорошо знаком (пока).

Ответы [ 2 ]

5 голосов
/ 17 июня 2011

Используйте другой уровень неявного (это действительно классы типов):

trait ListToJsonConverter[T] { 
  def asJson(l: List[T]) : String 
}
implicit object PeopleToJsonConverter extends ListToJsonConverter[Person] {...}
implicit object TopicToJsonConverter extends ListToJsonConverter[Topic] {...}
implicit object DefaultJsonConverter extends ListToJsonConverter[Any] {
  def asJson(l: List[Any]) = "[]"
}
implicit def topicsOrPeopleAsJson[T](list: List[T])(implicit ev : ListToJsonConverter[T]) = new {
  def asJson = ev.asJson(list)
}

Однако это может быть не совсем то, о чем вы просили. Конвертер будет выбран во время компиляции. Поэтому, если вы вызываете список людей, которых компилятор знает только как List [Any], он не будет работать должным образом.

4 голосов
/ 17 июня 2011

Почему бы не сделать это OO way?

trait JSONable {
  def toJSON:String
}

class Person
class Topics

implicit def persontoJSONable(p:Person) = new PersonSerializer(p)
implicit def topicToJSONable(t:Topic) = new PersonSerializer(t)

class PersonSerializer(p:Person) extends JSONable {
  override def toJSON = {
    //...
  }
}

class TopicSerializer(t:Topic) extends JSONable {
  override def toJSON = {
    //...
  }
}

def ListAsJSON[T <% JSONable](l:List[T]) = {
  l.map(_.toJSON)
}
...