繁体   English   中英

Scala:案例类不适用与手动实现和类型擦除

[英]Scala: Case class unapply vs a manual implementation and type erasure

我试图了解 Scala 对 Case Class 做了什么,使它们在某种程度上不受类型擦除警告的影响。

假设我们有以下简单的类结构。 它基本上是一个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编译器与案件类实现-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);

我无法给出完整的答案,但我可以告诉您,即使编译器为 case 类生成了unapply方法,当它在 case 类上进行模式匹配时,它也不会使用该 unapply 方法。 如果您尝试-Ybrowse:typer使用内置大小写匹配和unapply方法,您将看到生成的语法树(对于match )非常不同,具体取决于使用的语法树。 您还可以浏览后面的阶段并查看差异仍然存在。

为什么 Scala 不使用内置 unapply 我不确定,虽然这可能是你提出的原因。 而如何避开它为自己的unapply ,我不知道。 但这就是 Scala 似乎神奇地避免了这个问题的原因。

经过试验,显然这个版本的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相同,显然编译器能够在这个版本但不是你的。 不知道为什么。

我不能给你关于 case class match 和 unapply 之间区别的答案。 然而,在他们的书(Odersky、Spoon、Venners)“Scala 编程”第 2 章 26.6“提取器与案例类”中,他们写道:

“它们(案例类)通常导致比提取器更有效的模式匹配,因为 Scala 编译器可以比提取器上的模式更好地优化案例类上的模式。这是因为案例类的机制是固定的,而 unapply 或 unapplySeq 方法在提取器中几乎可以做任何事情。第三,如果您的案例类继承自密封基类,Scala 编译器将检查我们的模式匹配是否详尽,并且如果某个模式未涵盖某些可能值的组合,则会抱怨。没有这种详尽程度提取器可以使用支票。”

这对我说,乍一看,这两者的不同比人们预期的要大,但没有具体说明确切的区别是什么。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM