为什么Scala案例类复制方法仅使用案例类中定义的变量进行参数化?

为什么Scala案例类复制方法仅使用案例类中定义的变量进行参数化?,scala,Scala,为什么Scala案例类复制方法仅使用案例类中定义的变量进行参数化 基于问答的问题: 简短摘要-当使用字段定义trait并由case类扩展时,case类上的copy将仅使用case类中定义的变量创建新的类实例,而不使用扩展trait trait A { var list: List[Int] = List() def add(element: Int) = { list = element :: list } } case class B(str

为什么Scala案例类复制方法仅使用案例类中定义的变量进行参数化

基于问答的问题:

简短摘要-当使用字段定义trait并由case类扩展时,case类上的
copy
将仅使用case类中定义的变量创建新的类实例,而不使用扩展trait

 trait A {
    var list: List[Int] = List()

    def add(element: Int) = {
      list = element :: list
    }
  }

  case class B(str: String) extends A {
    val b = B("foo")
    println("B1: " + b.list)

    b.add(1)
    b.add(2)
    b.add(3)
    println("B2: " + b.list)

    val b2 = b.copy(str = "bar")
    println("B3: " + b.list)
    println("B4: " + b2.list)
  }

在这里,
B4:()
将是空的,而
B3:(3,2,1)
如果您真的想在复制后保留
列表的值(考虑到我在OP的评论中提到的可变性问题),您可以编写自己的
copy
方法

case class B(str: String) extends A {
  def copy(str: String = this.str, list: List[Int] = this.list): B = {
    val newB = B(str)
    list.reverse.foreach(newB.add)
    newB
  }
}

如果您真的想在复制后保留
list
的值(考虑到我在OP的评论中提到的可变性问题),您可以编写自己的
copy
方法

case class B(str: String) extends A {
  def copy(str: String = this.str, list: List[Int] = this.list): B = {
    val newB = B(str)
    list.reverse.foreach(newB.add)
    newB
  }
}

因为没有一种合理的方式来始终如一地做你想做的事情

例如,为
copy
生成的代码可以是

def copy(str: String = this.str, list: List[Int] = this.list): B = {
  val newB = B(str)
  newB.list = list
  newB
}

很好。现在,如果将
list
更改为private,或者将
val
更改为
var
,会发生什么情况?在这两种情况下,
newB.list=…
都不会编译,那么编译器应该生成什么代码呢?

因为没有合理的方法来一致地执行您想要的操作

例如,为
copy
生成的代码可以是

def copy(str: String = this.str, list: List[Int] = this.list): B = {
  val newB = B(str)
  newB.list = list
  newB
}


很好。现在,如果将
list
更改为private,或者将
val
更改为
var
,会发生什么情况?在这两种情况下,
newB.list=…
都不会编译,那么编译器应该生成什么代码?

请注意,如果要在其中嵌入可变状态,您应该重新考虑使用case类-在大多数情况下,case类应该是不可变的。顺便说一句。我运行了你的代码,B3是(3,2,1),而B4是空的(与你在问题中所说的相反)。我发现@cchantep在链接问题中的评论是一个完全令人满意的解释,引用:'没有自动/声明性的方法来处理这个问题,因为它是“不好的函数编程”'在为case类实现
copy
时,他们只是没有考虑这个用例,没有阻止
copy
-codegen,也没有发出错误或至少警告。也许你可以把它作为一个问题提交给scala编译器,这样它至少会输出一个关于可变性的警告…@JakubKozłowski——当然。这只是为了以身作则。关于B3&B$-谢谢。“当然应该是相反的。”安德烈·尤金——很有趣。所以你认为这只是一个“没有考虑过这个用例”的例子?请注意,如果你要在用例类中嵌入可变状态,你应该重新考虑用例类——在大多数情况下,用例类应该是不可变的。顺便说一句。我运行了你的代码,B3是(3,2,1),而B4是空的(与你在问题中所说的相反)。我发现@cchantep在链接问题中的评论是一个完全令人满意的解释,引用:'没有自动/声明性的方法来处理这个问题,因为它是“不好的函数编程”'在为case类实现
copy
时,他们只是没有考虑这个用例,没有阻止
copy
-codegen,也没有发出错误或至少警告。也许你可以把它作为一个问题提交给scala编译器,这样它至少会输出一个关于可变性的警告…@JakubKozłowski——当然。这只是为了以身作则。关于B3&B$-谢谢。“当然应该是相反的。”安德烈·尤金——很有趣。所以你认为这只是一个“没有考虑过这个用例”的例子?谢谢。是的,我明白。我的问题是,为什么case类的行为是这样的?可能是因为编译器只有在其主构造函数中定义了case类字段时才会以这种特殊的方式处理它们——请注意,只有第一个参数列表才会以这种方式处理。例如,如果字段是在第二个参数列表中定义的,您将无法使用copy方法修改字段。另一个突然出现的想法是,您可以将
列表
字段抽象为原始特征(如未初始化),然后使用
覆盖变量列表:列表[Int]=Nil
在case类的主构造函数中:
case类B(str:String,override-var-list:list[Int]=Nil)扩展了A
谢谢。是的,我明白。我的问题是,为什么case类的行为是这样的?可能是因为编译器只有在其主构造函数中定义了case类字段时才会以这种特殊的方式处理它们——请注意,只有第一个参数列表才会以这种方式处理。例如,如果字段是在第二个参数列表中定义的,您将无法使用copy方法修改字段。另一个突然出现的想法是,您可以将
列表
字段抽象为原始特征(如未初始化),然后使用
覆盖变量列表:列表[Int]=Nil
在案例类的主构造函数中:
案例类B(str:String,override-var-list:list[Int]=Nil)扩展了一个
Gotcha!谢谢,明白了!谢谢