我正在阅读结构化JSON,使用Play Frameworks的JSON Reads来构建带有案例类的对象图。

一个例子:

case class Foo (
                       id: Int,
                       bar_id: Int,
                       baz_id: Int,
                       x: Int,
                       y: String
                       )
{
  var bar: Bar = null
  var baz: Baz = null
}

构建Foo之后,我必须稍后再通过设置bar和baz对其进行装饰。这些是在其他JSON文件中定义的,并且仅在完成所有解析后才知道。但这意味着Foo不能一成不变。

在Scala中,制作不可变对象(immutable对象)然后进行修饰的版本,而又不需反复重复Foo的每个字段的“正确”方法是什么?

我知道几种感觉不对的方式:
  • 创建“bar:Option [Bar]”和“baz:Option [Baz]”案例类参数,然后我可以使用“copy”来创建Foo类的新版本,并将它们设置为某些值;但是然后我必须在每次访问它们时都要对其进行检查-低效,不安全,无法制作出可以保证具有正确结构
  • 的DecoratedFoo
  • 创建第二个case类,该类是第一个case结构的复制粘贴,但是添加了两个额外的修饰参数-但这意味着在定义中回显整个参数列表,并在创建它的实例时再次回显
  • Case类的继承显然是有争议的,无论如何,似乎也需要我在子类构造函数中重复每个参数?
  • 制作一个非案例父类(super class),列出常见案例类参数。然后在案例类中对其进行扩展。但这似乎仍然需要重复子类构造函数中的每个参数。
  • 我看到有人在谈论这个问题并在运行时使用反射来填充其修饰副本的基本属性的博客-这样可以避免回显,但是现在您没有类型安全性,将属性名称指定为字符串,开销等。

  • 当然,Scala必须有一种方法可以使人们从较简单的对象组成更复杂的不可变对象(immutable对象),而不必手工复制它们的每个部分?

    最佳答案

    结合Option和type参数,您可以标记案例类,并静态跟踪处理的字段是否为空:

    import scala.language.higherKinds
    
    object Acme {
      case class Foo[T[X] <: Option[X] forSome { type X }](a: Int,
                                                           b: String,
                                                           c: T[Boolean],
                                                           d: T[Double])
    
      // Necessary, Foo[None] won't compile
      type Unprocessed[_] = None.type
      // Just an alias
      type Processed[X] = Some[X]
    }
    

    用例示例:
    import Acme._
    
    val raw: Foo[Unprocessed] = Foo[Unprocessed](42, "b", None, None)
    
    def process(unprocessed: Foo[Unprocessed]): Foo[Processed] =
      unprocessed.copy[Processed](c = Some(true), d = Some(42d))
    
    val processed: Foo[Processed] = process(raw)
    
    // No need to pattern match, use directly the x from the Some case class
    println(processed.c.x)
    println(processed.d.x)
    

    我在当前项目中使用过一次。我遇到的主要问题是我希望Foo是协变的。

    或者,如果您不关心T上的界限,请执行以下操作:
    case class Foo[+T[_]](a: Int, b: String, c: T[Boolean], d: T[Double])
    

    那么当您需要Foo[Unprocessed]时,可以使用Foo[Processed]Foo[Option]
    scala> val foo: Foo[Option] = processed
    foo: Acme.Foo[Option] = Foo(42,b,Some(true),Some(42.0))
    

    09-30 14:12
    查看更多