我希望为案例类提供灵活的生成器。
case class ABC(a: String, b: String, c: String)
我想以任何顺序写作:
val genAbc = (new Gen).withB("b").withA("a").withC("c")
如果设置了所有参数,则能够将其转换为case类:
genAbc.toAbc
如果没有设置所有参数,我想得到编译错误:
(new Gen).withA("a").toAbc //<- error
有可能吗?
答案 0 :(得分:4)
Scala中有一个类型安全版本的构建器模式,请参阅http://blog.rafaelferreira.net/2008/07/type-safe-builder-pattern-in-scala.html
但是,如果你愿意放弃能够以任何顺序设置参数的要求,那么有一种更简单的方法:
scala> val abcBuilder = (Abc.apply _).curried
abcBuilder: String => (String => (String => Abc)) = <function1>
使用Abc
类的智能构造函数(Abc.apply
)创建 curried函数。 curried函数允许您分别传入每个参数。在此期间,您不断获得更多函数,这些函数采用一个参数并使您更接近Abc
实例。所以:
scala> abcBuilder("a")
res11: String => (String => Abc) = <function1>
scala> res11("b")
res12: String => Abc = <function1>
scala> res12("c")
res13: Abc = Abc(a,b,c)
这也是类型安全的(在某种程度上你使用不同的参数类型);所以例如abcBuilder(1)
会给你一个编译错误。
答案 1 :(得分:1)
你可以做这样的事情:
class Gen {
def withA(a: String) = GenWithA(a)
def withB(b: String) = GenWithB(b)
def withC(c: String) = GenWithC(c)
}
case class GenWithA(a: String) {
def withB(b: String) = GenWithAB(a, b)
def withC(c: String) = GenWithAC(a, c)
}
case class GenWithB(b: String) {
def withA(a: String) = GenWithAB(a, b)
def withC(c: String) = GenWithBC(b, c)
}
case class GenWithC(c: String) {
def withA(a: String) = GenWithAC(a, c)
def withB(b: String) = GenWithBC(b, c)
}
case class GenWithAB(a: String, b: String) {
def withC(c: String) = GenWithABC(a,b,c)
}
case class GenWithAC(a: String, c: String) {
def withB(b: String) = GenWithABC(a,b,c)
}
case class GenWithBC(b: String, c: String) {
def withA(a: String) = GenWithABC(a,b,c)
}
case class GenWithABC(a: String, b: String, c: String) {
def toABC = ABC(a,b,c)
}
这里有两个问题:
首先,当你开始超过3个参数时,你会得到组合爆炸。您可以在SBT中生成它,也可以使用宏或无形魔法生成它 可能但都是很多工作。
其次,这种代码无法按照您希望的方式进行类型检查:
val g0 = new Gen
val g1 = if (condition) g0.withA("a") else g0.withB("b")
val g2 = if (condition) g1.withB("b") else g1.withA("a")
val g3 = g2.withC("c")
从理论上讲,这总是产生一些有效的东西,但scala编译器 是不够聪明,弄清楚,所以你不能使用的不同部分 不同条件分支的建设者。
这并不会让你失望。如果您的目标是简单地以任何顺序编写参数,那么更简单的方法是将它们指定为命名参数:
ABC(
c = "C First",
b = "Then B",
a = "Finally A"
)
对于每个阶段都有类似的构建器,例如,在构建查询DSL时,但是对于构建数据案例类,它几乎总是笨重的过度杀伤
答案 2 :(得分:1)
也许你正在寻找像case class copy
方法那样简单的东西(你确实不能错过其中一个参数,你可以按随机顺序指定它们):
case class ABC(a: String, b: String, c: String)
val abc = ABC("a", "b", "c")
println(abc)
// ABC(a,b,c)
val acc = abc.copy(b = "c")
println(acc)
// ABC(a,c,c)