我正在阅读结构化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的每个字段的“正确”方法是什么?
我知道几种感觉不对的方式:
当然,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))