Scala:如何使案例类副本保留 list 信息

标签 scala manifest type-erasure type-parameter

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"

这是有效的,因为编译器推断 fooFoo[Int]所以它知道foo.aInt所以它可以调用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 中已知类,类型参数的类型参数不是。它知道ABarFooSubFooSubSubFoo ,但如果是 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/

相关文章:

scala - 附加可选元素列表

scala - 具有案例类的双向引用

android - 安装错误: INSTALL_PARSE_FAILED_MANIFEST_MALFORMED?

javascript - 显示缓存中存储的内容

java - 通用限制 hell : Bound Mismatch

Java::在运行时获取参数化类型名称

Java :How Generic Erasure Works

python - 如何在以下示例中使用 pyspark 折叠操作找到最大值?

scala - Http4s 客户端将实体编码为 x-www-form-urlencoded 递归

excel - 可以使用 VBA 控制 OfficeApps 吗?