Scala:Case类unapply vs手动实现和类型擦除

时间:2012-01-09 02:09:24

标签: 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]

你正试图像这样使用它:

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);

提前致谢

2 个答案:

答案 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],那么BD相同显然,编译器能够在这个版本中找到但不在你的版本中。不知道为什么。

答案 1 :(得分:6)

我无法就案例类匹配与未应用之间的区别给出答案。但是在他们的书中(Odersky,Spoon,Venners)和#34; Scala编程&#34;第2次chptr 26.6&#34;提取器与案例类别&#34;他们写道:

  

&#34;他们(案例类)通常会带来更有效的模式匹配   比提取器,因为Scala编译器可以优化模式   case类比提取器上的模式好得多。这是   因为案例类的机制是固定的,而不适用   或者提取器中的unapplySeq方法几乎可以做任何事情。第三,   如果您的case类继承自密封的基类Scala   编译器将检查我们的模式匹配的详尽性和意志   抱怨如果某些可能的值组合未被a覆盖   图案。提取器没有这种详尽的检查。&#34;

其中告诉我,这两者与乍一看的情况有所不同,但没有具体说明确切的差异。