Scala不可变对象和具有val字段的特征

Mar*_*usz 11 scala immutability traits scala-2.8

我想仅使用不可变对象构建我的域模型.但我也想使用具有val字段的特征并将一些功能移到特征中.请看下面的例子:

trait Versionable {
 val version = 0
 def incrementVersion = copy(version=version+1)
}
Run Code Online (Sandbox Code Playgroud)

不幸的是这样的代码不起作用 - 对于特性Versionable,复制方法是未知的.

我认为为每个特征和类生成复制方法会很好.这样的方法应该创建对象的浅拷贝,并使用与原始对象相同的类型返回它,并根据传递给方法的参数修改给定的字段.

所以在下面的例子中:

class Customer(val name: String) extends Versionable {
 def changeName(newName: String) = copy(name = newName)
}

val customer = new Customer("Scot")
Run Code Online (Sandbox Code Playgroud)

customer.changeName("McDonnald") 应该返回一个对象实例 Customer(version = 0, name = "McDonnald")

customer.incrementVersion 还应该返回一个对象实例 Customer(version = 1, name = "Scot")

据我所知,目前Scala中缺少此类功能不允许使用不可变类和特征而不会污染具有特征字段的类构造函数.在我的示例中,我不想将参数命名版本引入Customer类,因为版本处理的功能我想要封装在Versionable特性中.

我知道案例类中复制方法的功能以及使用默认参数在类中编写自己的复制方法的能力 - 但我认为这个功能并不能解决我的问题,因为在特征中不可能使用这种复制方法.现有功能的另一个缺点是使用复制方法的父类返回父类而不是实际复制的对象类.

我的问题:

1)您是否知道如何以优雅的方式处理上述示例.我对Scala很新,所以也许已经有了很好的解决方案.在我看来,优雅的解决方案应具有以下功能:

  • 不应该使用反射

  • 不应该使用序列化

  • 应该快

  • 应该在编译时可以验证

2)您如何考虑编写编译器插件来为我上面的示例生成复制方法的代码?是否可以使用编译器插件来做到这一点?您有任何示例或提示如何做到这一点?

Kev*_*ght 8

您最干净的解决方案可能是从某个实现逻辑中删除Versionable,并将其向下推送到案例类(该copy方法可供您使用).为version属性提供默认值以完成设计.

trait Versioned {
  def version : Int
  def nextVersion = version + 1 
}

case class Customer(name: String, version : Int = 0) extends Versioned {
  def withName(newName: String) = copy(name = newName, version = nextVersion)
}
Run Code Online (Sandbox Code Playgroud)

如果需要,还可以在某处为版本编号定义类型别名:

type Version = Int
val initialVersion = 0

trait Versioned {
  def version : Version
  def nextVersion = version + 1 
}

case class Customer(name: String, version : Version = initialVersion)
extends Versioned {
  def withName(newName: String) = copy(name = newName, version = nextVersion)
}
Run Code Online (Sandbox Code Playgroud)


Aar*_*rup 1

很难看出它是如何工作的以及如何与 Scala 的语义保持一致——特别是在特征中定义的不可变字段的语义。考虑版本化特征:

trait Versionable {
   val version = 0
}
Run Code Online (Sandbox Code Playgroud)

version该声明表明,除非被重写,版本字段的值将始终为 0。更改“不使用特征字段污染类构造函数”的值(即不显式重写版本字段)将违反这些语义。