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

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,显然编译器能够在这个版本中找出但不在你的版本中.不知道为什么.

  • 实际上,'unapply`被创建,因此用户可以复制`case class`提供的功能.还有一些其他地方官方解释事情是如何工作的,而不是编译器实际上是如何做的. (2认同)

Jan*_*rst 6

我不能给你关于case class match和unapply之间差异的答案.然而,在他们的书中(Odersky,Spoon,Venners)"Scala编程"第2节chptr 26.6"提取器与案例类"他们写道:

"它们(case类)通常会导致比提取器更有效的模式匹配,因为Scala编译器可以优化案例类的模式比提取器上的模式更好.这是因为case类的机制是固定的,而unapply或unapplySeq方法在一个提取器中几乎可以做任何事情.第三,如果你的case类继承自一个密封的基类,那么Scala编译器将检查我们的模式匹配的详尽性,如果一个模式没有涵盖可能值的某些组合,它会抱怨.没有这样的详尽性检查可用于提取器."

其中告诉我,这两者与乍看之下的预期有所不同,但并没有具体说明确切的差异.