如何最好地保留一个缓存的成员字段列表,每个成员字段对应于Scala中的一个案例类

时间:2014-05-27 19:19:22

标签: scala reflection

这是对以下问题的跟进:Fastest way to get the names of the fields of a case class in Scala

我正在尝试找到一种提供快速自定义序列化的简单方法(比如说(String, Object)的元组列表,它可以转换为生产中的db行或单元测试中的内存映射)对于Scala中的一类案例类,似乎保留一个类的字段的缓存列表可能是一种很有前途的方法。但是,我不确定最干净的方法。我知道我可以做以下事情:

case class Playlist(
  val id: Option[Long],
  val title: Option[String],
  val album: Option[String],
  val artist: Option[String],
  val songId: Option[UUID]) {
  def serialize = Playlist.fields.map(f => (f.getName, f.get(this)))
}

object Playlist {
  val empty = Playlist(None, None, None, None, None)
  val fields = Playlist.empty.getClass.getDeclaredFields.toList
  fields foreach { _.setAccessible(true) }
}

但是有一些我不喜欢的事情:

  1. 我不想使用伴侣类中的empty来获取缓存的字段列表
  2. 我不想为我想要这种序列化行为的每个案例类声明序列化逻辑。可能有几种方法来解决这个问题,但我不确定能够提供正确行为的最干净方式(担心混合反射和继承)。
  3. 在Scala中实现这一目标的最简洁方法是什么?

1 个答案:

答案 0 :(得分:2)

我认为将Class[_] -> fields的缓存映射与任何单个案例类分开是最简单的,例如在具有方法serialize(instance)的全局单例中。这样您就不必在要序列化的类中编写任何额外的代码。

另一种方法是创建一个特质来混合案例类'伴随对象,具有缓存的字段列表,以及用于添加serialize方法的隐式包装类。您可以使用隐式ClassTag初始化fields

abstract class MyCompanion[T](implicit ctag: ClassTag[T]) {
    private val fields = ctag.runtimeClass.getDeclaredFields.toList
    fields foreach { _.setAccessible(true) }
    implicit class AddSerializeMethod(obj: T) {
        def serialize = fields.map(f => (f.getName, f.get(obj)))
    }
}

case class C(...) { ... }
object C extends MyCompanion[C]

不幸的是,您似乎无法通过这种方式使AddSerializeMethod成为价值类。