問題無關使用Class
超過Tag
,所有跟在匹配對的情況下,物體(如IntegerTag
和StringTag
)匹配針對僅僅值(如TagOfInteger
,ClassOfInteger
和ClassOfString
)。
讓我們嘗試編譯您的第一個例子中的4個變種:
版本1:
版本2:
class Tag[T]
case class IntegerTag() extends Tag[Int]
case class StringTag() extends Tag[String]
def defaultValue[T](typ: Tag[T]): T = typ match {
case IntegerTag() => 0
case StringTag() => ""
}
版本3:
class Tag[T]
class IntegerTag extends Tag[Int]
class StringTag extends Tag[String]
def defaultValue[T](typ: Tag[T]): T = typ match {
case _: IntegerTag => 0
case _: StringTag => ""
}
版本4:
class Tag[T]
val IntegerTag: Tag[Int] = new Tag[Int]
val StringTag: Tag[String] = new Tag[String]
def defaultValue[T](typ: Tag[T]): T = typ match {
case IntegerTag => 0 // error: type mismatch
case StringTag => "" // error: type mismatch
}
如果你嘗試編譯他們,你會看到那個版本1,2和3編譯罰款,而第4版則沒有。 的原因是,在第1版,2和3,模式匹配允許編譯器肯定知道哪種類型T
:
在版本1中我們做case IntegerTag =>
。由於IntegerTag
是一個對象的情況下,我們知道肯定不能有任何實例等於IntegerTag
(除IntegerTag
本身)。所以,如果有匹配這裏的IntegerTag
運行時類型只能是IntegerTag
,延伸Tag[Int]
。因此我們可以安全地推斷出T = Int
。
在2版本中,我們做case IntegerTag() =>
。這裏IntegerTag
是一個案例類,正因爲如此,我們知道,只能在這裏BEA的比賽,如果typ
是IntegerTag
一個實例,它擴展Tag[Int]
。因此我們可以安全地推斷出T = Int
。
在3版本中,我們做case _: IntegerTag =>
。換句話說,我們明確地匹配IntegerTag
類型。因此,我們再次知道typ
是IntegerTag
類型,它擴展Tag[Int]
的,我們可以有把握地推斷T = Int
。
現在,第4版的問題是我們無法保證typ
的運行時類型。這是因爲,在這個版本中,我們只是做case IntegerTag =>
,其中IntegerTag
是val
。換句話說,將有一個匹配當且僅當typ == IntegerTag
。問題是,typ
等於IntegerTag
(或換句話說,typ.==(IntegerTag)
返回true)這一事實告訴我們關於typ
的運行時類型的任何內容。 事實上,人們可以很好地重新定義相等性,使它可以等於不相關類的實例(或簡單地等於相同泛型類的實例,但具有不同類型參數)。舉例,考慮:
val StringTag: Tag[String] = new Tag[String]
val IntegerTag: Tag[Int] = new Tag[Int] {
override def equals(obj: Any) = {
(obj.asInstanceOf[AnyRef] eq this) || (obj.asInstanceOf[AnyRef] eq StringTag)
}
}
println(StringTag == StringTag) // prints true
println(StringTag == IntegerTag) // prints false
println(IntegerTag == IntegerTag) // prints true
println(IntegerTag == StringTag) // prints true
IntegerTag == StringTag
返回true,這意味着如果我們通過StringTag
到方法defaultValue
,會有與case IntegerTag =>
匹配,即使在實際工作StringTag
是Tag[String]
一個實例,而不是Tag[Int]
。這表明確實存在與case IntegerTag =>
匹配的事實告訴我們沒有任何關於typ
的運行時類型。因此編譯器不能假設任何關於typ
的確切類型:我們只從其聲明的靜態類型知道它是Tag[T]
,但T
仍未知。