我正在尝试理解Scala对Case Classes的作用,使它们对类型擦除警告有所不同。
假设我们有以下简单的类结构。它基本上是Either
:
abstract class BlackOrWhite[A, B]
case class Black[A,B]( val left: A ) extends BlackOrWhite[A,B]
case class White[A,B]( val right: B ) extends BlackOrWhite[A,B]
你正试图像这样使用它:
object Main extends App {
def echo[A,B] ( input: BlackOrWhite[A,B] ) = input match {
case Black(left) => println( "Black: " + left )
case White(right) => println( "White: " + right )
}
echo( Black[String, Int]( "String!" ) )
echo( White[String, Int]( 1234 ) )
}
一切都编译并运行没有任何问题。但是,当我尝试自己实现unapply
方法时,编译器会发出警告。我使用了以下具有相同Main
类的类结构:
abstract class BlackOrWhite[A, B]
case class Black[A,B]( val left: A ) extends BlackOrWhite[A,B]
object White {
def apply[A,B]( right: B ): White[A,B] = new White[A,B](right)
def unapply[B]( value: White[_,B] ): Option[B] = Some( value.right )
}
class White[A,B]( val right: B ) extends BlackOrWhite[A,B]
使用-unchecked
标志进行编译会发出以下警告:
[info] Compiling 1 Scala source to target/scala-2.9.1.final/classes...
[warn] src/main/scala/Test.scala:41: non variable type-argument B in type pattern main.scala.White[_, B] is unchecked since it is eliminated by erasure
[warn] case White(right) => println( "White: " + right )
[warn] ^
[warn] one warning found
[info] Running main.scala.Main
现在,我理解了类型擦除,并且我试图用Manifests
来解决警告(到目前为止无济于事),但这两种实现有什么区别?案例类是否需要添加一些内容?可以用Manifests
来规避这个吗?
我甚至尝试通过scala编译器运行case类实现并打开-Xprint:typer
标志,但unapply
方法看起来非常像我预期的那样:
case <synthetic> def unapply[A >: Nothing <: Any, B >: Nothing <: Any](x$0: $iw.$iw.White[A,B]): Option[B] = if (x$0.==(null))
scala.this.None
else
scala.Some.apply[B](x$0.right);
提前致谢
答案 0 :(得分:13)
我无法给出完整的答案,但我可以告诉您,即使编译器为案例类生成unapply
方法,当它在案例类上进行模式匹配时,它也不会使用该unapply方法。如果您使用内置大小写匹配和-Ybrowse:typer
方法尝试unapply
,您会看到根据使用的语法树生成了非常不同的语法树(对于match
)。您还可以浏览后面的阶段,看看差异是否存在。
为什么Scala不使用内置的unapply我不确定,虽然它可能是你提出的原因。如何绕过它自己unapply
我不知道。但这就是斯卡拉似乎神奇地避免这个问题的原因。
经过实验,显然这个版本的unapply
有效,但我对此有点疑惑:
def unapply[A,B](value: BlackOrWhite[A,B]): Option[B] = value match {
case w: White[_,_] => Some(w.right)
case _ => None
}
你unapply
的难点在于,不管怎样,编译器必须确信如果White[A,B]
扩展BlackOrWhite[C,D]
,那么B
与D
相同显然,编译器能够在这个版本中找到但不在你的版本中。不知道为什么。
答案 1 :(得分:6)
我无法就案例类匹配与未应用之间的区别给出答案。但是在他们的书中(Odersky,Spoon,Venners)和#34; Scala编程&#34;第2次chptr 26.6&#34;提取器与案例类别&#34;他们写道:
&#34;他们(案例类)通常会带来更有效的模式匹配 比提取器,因为Scala编译器可以优化模式 case类比提取器上的模式好得多。这是 因为案例类的机制是固定的,而不适用 或者提取器中的unapplySeq方法几乎可以做任何事情。第三, 如果您的case类继承自密封的基类Scala 编译器将检查我们的模式匹配的详尽性和意志 抱怨如果某些可能的值组合未被a覆盖 图案。提取器没有这种详尽的检查。&#34;
其中告诉我,这两者与乍一看的情况有所不同,但没有具体说明确切的差异。