2013-03-05 123 views
2

下面的代碼工作得很好:爲什麼類型推斷在這種情況下失敗?

object InfDemo {  
    class Tag[T] 
    case object IntegerTag extends Tag[Int] 
    case object StringTag extends Tag[String] 
    val TagOfInteger: Tag[Int] = IntegerTag 

    def defaultValue[T](typ: Tag[T]): T = typ match { 
    case IntegerTag => 0 
    case StringTag => "" 
    // case TagOfInteger => 0 // this not works 
    }  
} 

,但下面的代碼將報告類型推斷錯誤:

object InfDemo2 { 
    val ClassOfInteger: Class[Integer] = classOf[Integer] 
    val ClassOfString : Class[String] = classOf[String] 
    def defaultValue[T](typ: Class[T]): T = typ match { 
     case ClassOfInteger => 0 
     case ClassOfString => "" 
    } 
} 

那麼究竟是什麼,這些代碼之間的區別,和Scala怎麼會在這裏做了類型推斷?

回答

6

問題無關使用Class超過Tag,所有跟在匹配對的情況下,物體(如IntegerTagStringTag)匹配針對僅僅值(如TagOfIntegerClassOfIntegerClassOfString)。

讓我們嘗試編譯您的第一個例子中的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的比賽,如果typIntegerTag一個實例,它擴展Tag[Int]。因此我們可以安全地推斷出T = Int

  • 在3版本中,我們做case _: IntegerTag =>。換句話說,我們明確地匹配IntegerTag類型。因此,我們再次知道typIntegerTag類型,它擴展Tag[Int]的,我們可以有把握地推斷T = Int

現在,第4版的問題是我們無法保證typ的運行時類型。這是因爲,在這個版本中,我們只是做case IntegerTag =>,其中IntegerTagval。換句話說,將有一個匹配當且僅當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 =>匹配,即使在實際工作StringTagTag[String]一個實例,而不是Tag[Int]。這表明確實存在與case IntegerTag =>匹配的事實告訴我們沒有任何關於typ的運行時類型。因此編譯器不能假設任何關於typ的確切類型:我們只從其聲明的靜態類型知道它是Tag[T],但T仍未知。

相關問題