2013-03-05 180 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仍未知。

相关问题