为什么Scala案例类复制方法仅使用案例类中定义的变量进行参数化?
为什么Scala案例类复制方法仅使用案例类中定义的变量进行参数化 基于问答的问题: 简短摘要-当使用字段定义trait并由case类扩展时,case类上的为什么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
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!谢谢,明白了!谢谢