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) 

我觉得这代码应该抛出的MatchError代替ClassCastException。或者 scalac应该警告这段代码。这是scalac bug吗?

斯卡拉2.11.5

编辑:

斯卡拉2.9.3警告如下。但不是2.10

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] = [email protected] 
string: X[String] = [email protected] 
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 
+0

貌似类型擦除问题,斯卡拉不能X [INT]'和'X [字符串]''之间的区别,所以比赛是积极的,但函数返回类型是'Int',而值是'String'。 –

回答

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] = [email protected] 

scala> val s = new X("a") 
s: X[String] = [email protected] 

scala> val pf: PartialFunction[Any, Int] = { case o @ int() => o.v } 
pf: PartialFunction[Any,Int] = <function1> 

scala> pf(s) 
scala.MatchError: [email protected] (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 } 
                   ^
0

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

首先,我要与X[C]替换Any

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: [email protected] (of class X) 
scala.MatchError: [email protected] (of class X)