我只想使用不可变的对象来构造我的域模型。但是我也想在val字段中使用特性,并将一些功能转移到特性上。请查看以下示例:
trait Versionable {
val version = 0
def incrementVersion = copy(version=version+1)
}不幸的是,这样的代码不工作-复制方法是未知的特性可验证。
我认为为每个特性和类生成复制方法是很好的。这种方法应该创建对象的浅拷贝,并使用与原始对象相同的类型返回对象,并根据传递给方法的参数修改给定的字段。
因此,在以下示例中:
class Customer(val name: String) extends Versionable {
def changeName(newName: String) = copy(name = newName)
}
val customer = new Customer("Scot")customer.changeName("McDonnald")应该返回一个对象实例Customer(version = 0, name = "McDonnald")
和
customer.incrementVersion还应该返回一个对象实例Customer(version = 1, name = "Scot")
据我所知,目前Scala中缺乏这样的功能,不允许使用不可变的类和特征,而不使用带有属性字段的类构造函数。在我的示例中,我不想向Customer类引入名为version的参数,因为我希望将版本处理功能封装在可验证特性中。
我知道copy方法在case类中的功能,以及使用默认参数在类中编写自己的复制方法的能力--但我认为这种功能不能解决我的问题,因为在特性中不可能使用这种复制方法。现有功能的另一个缺点是,使用复制方法的父类返回父类,而不是实际复制的对象类。
我的问题:
1)你知道如何以优雅的方式处理上面的例子吗?我对Scala非常陌生,所以可能已经有了很好的解决方案。在我看来,优雅的解决方案应该具有以下特性:
serialization
2)您认为如何编写编译器插件来为我的上述示例生成复制方法的代码?使用编译器插件可以做到这一点吗?你有怎样做的例子或建议吗?
发布于 2010-09-14 14:05:40
最干净的解决方案可能是从Versionable中删除一些实现逻辑,并将其向下推到case类(在这种情况下,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)
}如果需要,还可以在某个地方为版本号定义类型别名:
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)
}发布于 2010-08-12 15:56:06
这是另一种解决方案,就像OP的代码一样,不起作用。但是,它可能为扩展该语言提供了一个更简单(也更有用)的起点。
trait Versionable[T] {
self: { def copy(version: Int): T } =>
val version = 0
def incrementVersion = copy(version = version + 1)
}
case class Customer(name: String, override val version: Int)
extends Versionable[Customer] {
def changeName(newName: String) = copy(name = newName)
}如果编译器认为Customer类的copy方法符合Versionable的自类型注释中定义的方法,这似乎是使用命名和默认参数的一种自然方式,那么代码就可以工作。
发布于 2010-08-12 07:28:06
尽管您说过,您不想使用case类。下面是一个使用它们的解决方案:
case class Version(number: Int) {
override def toString = "v" + number
def next = copy(number+1)
}
case class Customer(name: String, version: Version = Version(0)) {
def changeName(newName: String) = copy(newName)
def incrementVersion = copy(version = version.next)
}现在你可以这样做了:
scala> val customer = new Customer("Scot")
customer: Customer = Customer(Scot,v0)
scala> customer.changeName("McDonnald")
res0: Customer = Customer(McDonnald,v0)
scala> customer.incrementVersion
res1: Customer = Customer(Scot,v1)
scala> customer // not changed (immutable)
res2: Customer = Customer(Scot,v0)https://stackoverflow.com/questions/3459630
复制相似问题