请看以下玩具示例:
case class Person(name: String, address: Person#Address = null) {
case class Address(street: String, city: String, state: String) {
def prettyFormat = s"To $name of $city" // note I use name here
}
def setAddress(street: String, city: String, state: String): Person =
copy(address=Address(street,city,state))
def setName(n: String): Person = copy(name=n)
}
你看到那里有错误吗?是的,以下代码将在两种情况下打印相同的消息 (John):
val p1 = Person("John").setAddress("Main", "Johntown", "NY")
println(p1.address.prettyFormat) // prints To John of Johntown
val p2 = p1.setName("Jane")
println(p2.address.prettyFormat) // prints To John of Johntown
这自然是因为在 set 方法中保留了 Address 中的 $outer 引用,所以 p2 内部对象仍然引用 John。该问题可以通过以下方式或通过重新创建 Address 对象来解决(如果我们在案例类中预先制作了复制构造函数,那不是很好吗?):
def setName(n: String) = copy(name=n).setAddress(address.street,address.city,address.state)
然而,如果有几个像这样的内部对象和数十个像 setName 这样的方法,问题就会变得更加烦人。所以我的结论是不变性和类内部类相互不兼容 .
问题:对于嵌套不可变对象(immutable对象)的结构,是否存在设计模式或有用的习惯用法,其中内部对象需要访问外部对象才能完成工作。
到目前为止,我已经考虑将 person 隐式传递给 prettyFormat 或将内部方法包装到 Reader monad 中,因此当前 person 将应用于 prettyFormat 返回的 monad。还有什么好主意吗?
最佳答案
这并不是说不变性和类内部类相互不兼容,而是当您创建内部地址类时,它会绑定(bind)到该 Person 实例(否则您将使用静态内部类,即在伴随对象中定义 Address )。
您的问题更多在于 copy
的语义。为不考虑内部类的案例类提供的方法。因此,要么放弃不变性,要么在修改时创建一个真正的新人:
def setName(n: String): Person = Person(n, street, city, state)
请注意,我不应该直接通过
Address
实例到 Person()
,你的定义是每个地址类型是一个人的一部分,只对那个人有意义,所以它不能存在于那个人之外,我不能将它从外部传递给正在创建的新人.同样,如果不是这种情况,那么您需要重新考虑使用不同语义的结构。就个人而言,我认为以下内容作为对域的描述更加清晰/直观:
case class Address(street: String, city: String, state: String)
case class Person(name: String, address: Address) {
def prettyFormat = s"To $name of ${address.city}"
}
然后您可以创建地址/人员的副本,而无需担心和完全不变。
关于scala - Scala 中的内部类与不变性,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/32305016/