Nyc*_*cto 19 scala pattern-matching type-erasure case-class unapply
我试图了解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]
Run Code Online (Sandbox Code Playgroud)
而你正试图像这样使用它:
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 ) )
}
Run Code Online (Sandbox Code Playgroud)
一切都编译和运行没有任何问题.但是,当我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]
Run Code Online (Sandbox Code Playgroud)
使用该-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
Run Code Online (Sandbox Code Playgroud)
现在,我理解类型擦除,我试图绕过警告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);
Run Code Online (Sandbox Code Playgroud)
提前致谢
Owe*_*wen 13
我无法给出完整的答案,但我可以告诉你,即使编译器unapply为case类生成一个方法,当它在case类上进行模式匹配时,它也不会使用那个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
}
Run Code Online (Sandbox Code Playgroud)
你的困难unapply在于,不管怎样,编译器必须确信如果White[A,B]扩展a BlackOrWhite[C,D]然后B是相同的D,显然编译器能够在这个版本中找出但不在你的版本中.不知道为什么.
我不能给你关于case class match和unapply之间差异的答案.然而,在他们的书中(Odersky,Spoon,Venners)"Scala编程"第2节chptr 26.6"提取器与案例类"他们写道:
"它们(case类)通常会导致比提取器更有效的模式匹配,因为Scala编译器可以优化案例类的模式比提取器上的模式更好.这是因为case类的机制是固定的,而unapply或unapplySeq方法在一个提取器中几乎可以做任何事情.第三,如果你的case类继承自一个密封的基类,那么Scala编译器将检查我们的模式匹配的详尽性,如果一个模式没有涵盖可能值的某些组合,它会抱怨.没有这样的详尽性检查可用于提取器."
其中告诉我,这两者与乍看之下的预期有所不同,但并没有具体说明确切的差异.