Scala:案例类取消应用与手动实现和类型擦除
我试图了解 Scala 对案例类的作用,使它们在某种程度上不受类型擦除警告的影响。
假设我们有以下简单的类结构。它基本上是一个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
来规避这个问题吗?
我什至尝试在打开 -Xprint:typer
标志的情况下通过 scala 编译器运行案例类实现,但 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);
I'm trying to understand what Scala does with Case Classes that makes them somehow immune to type erasure warnings.
Let's say we have the following, simple class structure. It's basically an 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]
And you're trying to use it like this:
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 ) )
}
Everything compiles and runs without any problems. However, when I try implementing the unapply
method myself, the compiler throws a warning. I used the following class structure with the same Main
class above:
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]
Compiling that with the -unchecked
flag issues the following warning:
[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
Now, I understand type erasure and I've tried to get around the warning with Manifests
(to no avail so far), but what is the difference between the two implementations? Are case classes doing something that I need to add in? Can this be circumvented with Manifests
?
I even tried running the case class implementation through the scala compiler with the -Xprint:typer
flag turned on, but the unapply
method looks pretty much like I expected:
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);
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(2)
我无法给出完整的答案,但我可以告诉您,即使编译器为案例类生成
unapply
方法,当它在案例类上进行模式匹配时,它不会使用该 unapply 方法。如果您尝试使用内置大小写匹配和unapply
方法来尝试-Ybrowse:typer
,您将看到生成了一个非常不同的语法树(对于match
代码>) 取决于所使用的。您还可以浏览后面的阶段并看到差异仍然存在。我不确定为什么 Scala 不使用内置的 unapply,尽管这可能是出于您提出的原因。我不知道如何为自己的
unapply
绕过它。但这就是 Scala 似乎神奇地避免了这个问题的原因。经过实验,显然这个版本的
unapply
有效,尽管我对为什么有点困惑:unapply
的困难在于,编译器必须以某种方式确信,如果aWhite[A,B]
扩展BlackOrWhite[C,D]
则B
与D
相同,显然编译器能够在这个版本中计算出来,但在你的。不知道为什么。I cannot give a complete answer, but I can tell you that even though the compiler generates an
unapply
method for case classes, when it pattern matches on a case class it does not use that unapply method. If you try-Ybrowse:typer
using both builtin case matching and yourunapply
method, you will see a very different syntax tree is produced (for thematch
) depending on which is used. You can also browse the later phases and see that the difference remains.Why Scala does not use the builtin unapply I am not sure, though it may be for the reason you bring up. And how to get around it for your own
unapply
I have no idea. But this is the reason Scala seems to magically avoid the problem.After experimenting, apparently this version of
unapply
works, though I'm a bit confused about why:The difficulty with your
unapply
is that somehow the compiler has to be convinced that if aWhite[A,B]
extends aBlackOrWhite[C,D]
thenB
is the same asD
, which apparently the compiler is able to figure out in this version but not in yours. Not sure why.我无法给您有关案例类匹配和取消申请之间区别的答案。然而,在他们的书(Odersky、Spoon、Venners)“Scala 编程”第 2 章 chptr 26.6“提取器与案例类”中,他们写道:
这对我来说,这两者比乍一看所预期的有更大的不同,但没有具体说明确切的差异是什么。
I can't give you the answer on the difference between case class match and unapply. However in their book (Odersky, Spoon, Venners) "Programming in Scala" 2nd chptr 26.6 "Extractors versus case classes" they write:
Which says to me that the two are more different than one would expect at first glance, however without being specific on what the exact differences are.