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

我认为此代码应抛出 aMatchError而不是ClassCastException. 或者scalac 应该警告此代码。

斯卡拉 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
4

2 回答 2

1

代码中的使用Any是一个很大的气味。当您丢弃类型信息时,您不能对编译器产生错误。

首先,我将替换AnyX[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)
于 2015-01-13T17:50:56.530 回答
0

他们竭尽全力:

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 }
                                                                  ^
于 2015-01-13T07:38:18.093 回答