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)