对象构造的组合性
这里的基本问题是,在 Scala 以及我所知道的所有其他语言中,对象构造不组合。考虑两个抽象操作op1和op2,其中op1 使属性 p1 为真,而 op2 使属性 p2 对。如果 op1 ○ op2 使 p1 和 p2 都为真。 (简而言之,属性也需要组合操作,例如和等连词。)
让我们考虑new 操作和new A(): A 的属性,即通过调用new A 创建的对象的类型为A。 new 操作缺乏组合性,因为在 Scala 中没有操作/语句/函数f 允许您组合new A 和new B 使得f(new A, new B): A with B。 (简而言之,不要想太多A 和B 是否必须是类或特征或接口等等)。
使用超级调用组合
超级调用通常可用于组合操作。考虑以下示例:
abstract class A { def op() {} }
class X extends A {
var x: Int = 0
override def op() { x += 1 }
}
trait T extends A {
var y: String = "y"
override def op() { super.op(); y += "y" }
}
val xt = new X with T
println(s"${xt.x}, ${xt.y}") // 0, y
xt.op()
println(s"${xt.x}, ${xt.y}") // 1, yy
设X.op 的属性为“x 加一”,设T.op 的属性为“y 的长度加一”。用超级调用实现的组合满足这两个特性。万岁!
你的问题
假设您正在使用一个类A,该类具有一个字段x,一个特征T1 具有一个字段y,另一个特征T2 具有一个字段z。您想要的是以下内容:
val obj: A with T1 with T2
// update obj's fields
val objC: A with T1 with T2 = obj.copy()
assert(obj.x == objC.x && obj.y == objC.y && obj.z == objC.z)
您的问题可以分为两个与组合性相关的子问题:
创建所需类型的新实例。这应该通过construct 方法来实现。
初始化新创建的对象,使其所有字段都具有与源对象。这应该通过initialise 方法来实现。
第二个问题可以通过超级调用解决,第一个不能。我们将首先考虑更容易的问题(第二个)。
对象初始化
让我们假设construct 方法按预期工作并产生正确类型的对象。类似于初始示例中op 方法的组合,我们可以实现initialise,这样每个类/特征A、T1 和T2 通过将它知道的字段设置为实现initialise(objC)来自this 的对应值(单个效果),并通过调用super.initialise(objC) 来组合这些单个效果。
对象创建
据我所知,没有办法组合对象创建。如果要创建A with T1 with T2 的实例,则必须在某处执行语句new A with T1 with T2。如果超级调用在这里可以提供帮助,那么类似
val a: A = new A // corresponds to the "deepest" super-call
val at1: A with T1 = a with new T1
是必要的。
可能的解决方案
我实现了一个基于抽象类型成员和显式混合类(class AWithT1WithT2 extends A with T1 with T2; val a = new AWithT1WithT2 而不是val a = new A with T1 with T2)的解决方案(see this gist)。它可以工作并且是类型安全的,但它既不是特别好也不简洁。显式 mixin 类是必需的,因为 new A with T1 with T2 的 construct 方法必须能够命名它创建的类型。
其他类型安全性较低的解决方案可能是可能的,例如,通过asInstanceOf 或反射进行转换。不过,我还没有尝试过类似的方法。
Scala 宏也可能是一种选择,但我还没有使用它们,因此对它们了解不够。编译器插件可能是另一个重量级选项。