首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >带val字段的Scala不可变对象和特征

带val字段的Scala不可变对象和特征
EN

Stack Overflow用户
提问于 2010-08-11 14:51:34
回答 5查看 6.8K关注 0票数 11

我只想使用不可变的对象来构造我的域模型。但是我也想在val字段中使用特性,并将一些功能转移到特性上。请查看以下示例:

代码语言:javascript
复制
trait Versionable {
 val version = 0
 def incrementVersion = copy(version=version+1)
}

不幸的是,这样的代码不工作-复制方法是未知的特性可验证。

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

因此,在以下示例中:

代码语言:javascript
复制
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

  • should
  • 不应该使用反射
  • 不应该使用
  • 是快速的
  • 在编译时应该是可验证的

2)您认为如何编写编译器插件来为我的上述示例生成复制方法的代码?使用编译器插件可以做到这一点吗?你有怎样做的例子或建议吗?

EN

回答 5

Stack Overflow用户

发布于 2010-09-14 14:05:40

最干净的解决方案可能是从Versionable中删除一些实现逻辑,并将其向下推到case类(在这种情况下,copy方法将对您可用)。给version属性一个默认值来完成设计。

代码语言:javascript
复制
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)
}

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

代码语言:javascript
复制
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)
}
票数 8
EN

Stack Overflow用户

发布于 2010-08-12 15:56:06

这是另一种解决方案,就像OP的代码一样,不起作用。但是,它可能为扩展该语言提供了一个更简单(也更有用)的起点。

代码语言:javascript
复制
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的自类型注释中定义的方法,这似乎是使用命名和默认参数的一种自然方式,那么代码就可以工作。

票数 4
EN

Stack Overflow用户

发布于 2010-08-12 07:28:06

尽管您说过,您不想使用case类。下面是一个使用它们的解决方案:

代码语言:javascript
复制
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)
}

现在你可以这样做了:

代码语言:javascript
复制
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)
票数 3
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/3459630

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档