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在这里是如何进行类型推断的呢?

4

1 回答 1

6

Class该问题与使用over无关,Tag而是与匹配 case 对象(例如IntegerTagand StringTag)而不是匹配单纯的值(例如TagOfInteger, ClassOfIntegerand ClassOfString)有关。

让我们尝试编译第一个示例的 4 个变体:

版本 1:

class Tag[T]
case object IntegerTag extends Tag[Int]
case object StringTag extends Tag[String]
def defaultValue[T](typ: Tag[T]): T = typ match {
  case IntegerTag => 0
  case StringTag => ""
}     

版本 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 是一个case对象,我们肯定知道不可能有任何实例等于IntegerTag(除了IntegerTag它自己)。所以如果这里有匹配,则运行时类型IntegerTag只能是IntegerTag,也就是extends Tag[Int]。因此我们可以有把握地推断出T = Int

  • 在第 2 版中,我们这样做了case IntegerTag() =>。这是一个案例类,因此我们知道如果是扩展的实例,则IntegerTag此处只能有匹配。因此我们可以有把握地推断出。typIntegerTagTag[Int]T = Int

  • 在第 3 版中,我们这样做了case _: IntegerTag =>。换句话说,我们明确匹配IntegerTag类型。所以我们再一次知道那typ是类型IntegerTag,它扩展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给 method defaultValue,将会匹配 with case IntegerTag =>,即使实际是 of而不是 ofStringTag的实例。这表明确实存在匹配的事实并没有告诉我们关于. 因此编译器无法假设 的确切类型:我们仅从其声明的静态类型中知道它是 a但仍然未知。Tag[String]Tag[Int]case IntegerTag =>typtypTag[T]T

于 2013-03-05T12:58:35.310 回答