为什么类型推断在这种情况下失败?
下面的代码工作得很好:为什么类型推断在这种情况下失败?
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怎么会在这里做了类型推断?
问题无关使用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
仍未知。