【问题标题】:Is this scalac bug?这是scalac错误吗?
【发布时间】:2015-03-10 22:04:54
【问题描述】:
class X[A](val value: A){
  def unapply[B <: A](x: X[B]) = true
}

object Main extends App {
  val int = new X(1)
  val string = new X("a")
  val pf: PartialFunction[Any, Int] = { case o @ int() => o.value }
  println(pf(string) + 1)
}
java.lang.ClassCastException: java.lang.String cannot be cast to java.lang.Integer
    at scala.runtime.BoxesRunTime.unboxToInt(BoxesRunTime.java:105)

我认为这段代码应该抛出一个MatchError 而不是ClassCastException。或 scalac 应该警告此代码。

Scala 2.11.5

已编辑:

Scala 2.9.3 警告如下。但不是 2.10、2.11

Main.scala:8: warning: non variable type-argument B in type pattern X[B] is unchecked since it is eliminated by erasure
  val pf: PartialFunction[Any, Int] = { case o @ int() => o.value }
                                                    ^

我从一开始就理解类型擦除。

换句话说,pf.isDefinedAt(string) 返回 true 但 pf.apply(string) 抛出 ClassCastException

Welcome to Scala version 2.11.5 (Java HotSpot(TM) 64-Bit Server VM, Java 1.7.0_67).
Type in expressions to have them evaluated.
Type :help for more information.

scala> :paste
// Entering paste mode (ctrl-D to finish)

class X[A](val value: A){
  def unapply[B <: A](x: X[B]) = true
}

val int = new X(1)
val string = new X("a")
val pf: PartialFunction[Any, Int] = { case o @ int() => o.value }

// Exiting paste mode, now interpreting.

defined class X
int: X[Int] = X@4f3cb3fc
string: X[String] = X@1a4c8e08
pf: PartialFunction[Any,Int] = <function1>

scala> pf.isDefinedAt(string)
res0: Boolean = true

scala> pf.apply(string)
java.lang.ClassCastException: java.lang.String cannot be cast to java.lang.Integer
  at scala.runtime.BoxesRunTime.unboxToInt(BoxesRunTime.java:105)
  ... 33 elided

【问题讨论】:

  • 看起来像类型擦除问题,Scala无法区分X[Int]和X[String],所以匹配是正数,但是函数返回类型是Int而值是String。跨度>

标签: scala pattern-matching classcastexception partialfunction


【解决方案1】:

Any 的使用是代码中的一大异味。当你丢弃你的类型信息时,你不能责怪编译器。

首先,我将Any 替换为X[C]:

def pf[C]: PartialFunction[X[C], Int] = { case o @ int() => o.value }

这会产生以下编译错误:

[error] Foo.scala:12: inferred type arguments [C] do not conform to method unapply's type parameter bounds [B <: Int]
[error]   def pf[C]: PartialFunction[X[C], Int] = { case o @ int() => o.value }
[error]                                                      ^

如果您想将类型检查延迟到运行时,您需要在某处捕获类型信息。我们可以在X 中这样做:

import scala.reflect.runtime.universe._

class X[A: TypeTag](val value: A) {
  val typeTag = typeOf[A]
  def unapply[B](x: X[B]): Boolean =
    x.typeTag <:< this.typeTag
}

现在我们必须告诉编译器如果匹配成功,C 实际上是Int。

def pf[C]: PartialFunction[X[C], Int] = { case o @ int() => o.value.asInstanceOf[Int] }
println(pf(int) + 1)  
println(pf(string) + 1)

这似乎有效:

[info] Running Main 
2
[error] (run-main-7) scala.MatchError: X@1c34aaaa (of class X)
scala.MatchError: X@1c34aaaa (of class X)

【讨论】:

    【解决方案2】:

    他们竭尽全力:

    scala> import reflect._
    import reflect._
    
    scala> class X[A: ClassTag](val v: A) { def unapply[B <: A](x: X[B]) = x.v match {
         |   case _: A => true
         |   case _    => false } }
    defined class X
    
    scala> val int = new X(1)
    int: X[Int] = X@2a5ca609
    
    scala> val s = new X("a")
    s: X[String] = X@4667ae56
    
    scala> val pf: PartialFunction[Any, Int] = { case o @ int() => o.v }
    pf: PartialFunction[Any,Int] = <function1>
    
    scala> pf(s)
    scala.MatchError: X@4667ae56 (of class X)
      at scala.PartialFunction$$anon$1.apply(PartialFunction.scala:253)
      at scala.PartialFunction$$anon$1.apply(PartialFunction.scala:251)
      at $anonfun$1.applyOrElse(<console>:12)
      at scala.runtime.AbstractPartialFunction.apply(AbstractPartialFunction.scala:36)
      ... 33 elided
    
    scala> pf(int)
    res1: Int = 1
    

    但我猜你的意思是,如果他们静态地抱怨这个,他们为什么不为我插入一个 instanceof 测试?至少在他们可以的时候?

    scala> val pf: PartialFunction[X[_], Int] = { case o @ int() => o.v }
    <console>:12: error: inferred type arguments [_$1] do not conform to method unapply's type parameter bounds [B <: Int]
           val pf: PartialFunction[X[_], Int] = { case o @ int() => o.v }
                                                           ^
    <console>:12: error: type mismatch;
     found   : _1
     required: Int
           val pf: PartialFunction[X[_], Int] = { case o @ int() => o.v }
                                                                      ^
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2012-03-18
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多