我正在阅读结构化JSON,使用Play Frameworks' JSON读取以构建具有案例类的对象图。
一个例子:
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中创建一个不可变对象,然后是它的装饰版本,而不是一遍又一遍地重复Foo的每个字段?
我知道有几种感觉不对的方法:
当然,Scala必须有办法让人们用更简单的对象组成更复杂的不可变对象,而不必手工复制它们的每一部分?
答案 0 :(得分:1)
另一种策略可能是创建另一个案例类:
case class Foo(
id: Int,
bar_id: Int,
baz_id: Int,
x: Int,
y: String
)
case class ProcessedFoo(
foo: Foo,
bar: Bar,
baz: Baz
)
答案 1 :(得分:1)
您可以为已处理的类型引入新特征,扩展该特征的类以及隐式转换:
case class Foo(bar: Int)
trait HasBaz {
val baz: Int
}
class FooWithBaz(val foo: Foo, val baz: Int) extends HasBaz
object FooWithBaz {
implicit def innerFoo(fwb: FooWithBaz): Foo = fwb.foo
implicit class RichFoo(val foo: Foo) extends AnyVal {
def withBaz(baz: Int) = new FooWithBaz(foo, baz)
}
}
那么你可以这样做:
import FooWithBaz._
Foo(1).withBaz(5)
而且,尽管withBaz
返回FooWithBaz
,但由于隐式转换,我们可以在必要时将返回值视为Foo
。
答案 2 :(得分:1)
结合Option
和类型参数,您可以标记您的案例类,并静态地跟踪已处理的字段是否为空:
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))