A案例类copy()
方法应该制作实例的相同副本,并按名称替换任何字段。当案例类具有带有 list 的类型参数时,这似乎失败了。副本丢失了其参数类型的所有知识。
case class Foo[+A : Manifest](a: A) {
// Capture manifest so we can observe it
// A demonstration with collect would work equally well
def myManifest = implicitly[Manifest[_ <: A]]
}
case class Bar[A <: Foo[Any]](foo: A) {
// A simple copy of foo
def fooCopy = foo.copy()
}
val foo = Foo(1)
val bar = Bar(foo)
println(bar.foo.myManifest) // Prints "Int"
println(bar.fooCopy.myManifest) // Prints "Any"
为什么
Foo.copy
丢失参数 list ,如何让它保留它?
最佳答案
几个 Scala 特性相互作用以产生这种行为。第一件事是Manifest
s 不仅附加到构造函数的 secret 隐式参数列表中,还附加到复制方法上。众所周知,case class Foo[+A : Manifest](a: A)
只是语法糖case class Foo[+A](a: A)(implicit m: Manifest[A])
但这也会影响复制构造函数,看起来像这样def copy[B](a: B = a)(implicit m: Manifest[B]) = Foo[B](a)(m)
所有这些implicit m
s 由编译器创建并通过隐式参数列表发送到方法。
只要使用 copy
就可以了。编译器知道的地方的方法Foo
s 类型参数。例如,这将在 Bar 类之外工作:
val foo = Foo(1)
val aCopy = foo.copy()
println(aCopy.myManifest) // Prints "Int"
这是有效的,因为编译器推断
foo
是 Foo[Int]
所以它知道foo.a
是 Int
所以它可以调用copy
像这样:val aCopy = foo.copy()(manifest[Int]())
(注意
manifest[T]()
是一个创建 T
类型的 list 表示的函数,例如 Manifest[T]
带有大写“M”。未显示将默认参数添加到 copy
中。)它也有效在 Foo
内类,因为它已经具有创建类时传入的 list 。它看起来像这样:case class Foo[+A : Manifest](a: A) {
def myManifest = implicitly[Manifest[_ <: A]]
def localCopy = copy()
}
val foo = Foo(1)
println(foo.localCopy.myManifest) // Prints "Int"
然而,在原始示例中,它在
Bar
中失败了。 class 因为第二个特点:而 Bar
的类型参数在 Bar
中已知类,类型参数的类型参数不是。它知道A
在 Bar
是 Foo
或 SubFoo
或 SubSubFoo
,但如果是 Foo[Int]
则不会或 Foo[String]
.当然,这是 Scala 中众所周知的类型删除问题,但即使该类似乎没有对 foo
类型执行任何操作,它也会在此处显示为问题。 s 类型参数。但它是,记住每次都会有一个 list 的 secret 注入(inject) copy
被调用,并且那些 list 会覆盖之前存在的 list 。由于Bar
类不知道是 foo
的类型参数也就是说,它只是创建了 Any
的 list 并像这样发送:def fooCopy = foo.copy()(manifest[Any])
如果一个人可以控制
Foo
类(例如,它不是 List
)然后一个解决方法是通过在 Foo 类中添加一个可以进行正确复制的方法来完成所有复制,例如 localCopy
上面,并返回结果:case class Bar[A <: Foo[Any]](foo: A) {
//def fooCopy = foo.copy()
def fooCopy = foo.localCopy
}
val bar = Bar(Foo(1))
println(bar.fooCopy.myManifest) // Prints "Int"
另一种解决方案是添加
Foo
s 类型参数作为 Bar
的显示类型参数:case class Bar[A <: Foo[B], B : Manifest](foo: A) {
def fooCopy = foo.copy()
}
但是,如果类层次结构很大(即更多成员具有类型参数,并且这些类也具有类型参数),则这种扩展性很差,因为每个类都必须具有其下每个类的类型参数。在尝试构造
Bar
时,它似乎也使类型推断异常。 :val bar = Bar(Foo(1)) // Does not compile
val bar = Bar[Foo[Int], Int](Foo(1)) // Compiles
关于Scala:如何使案例类副本保留 list 信息,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/11918016/