下面的代码运行良好:
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是如何进行类型推断的呢?
最佳答案
问题与在 Tag
上使用 Class
无关,而与匹配 case 对象(例如 IntegerTag
和 StringTag
) 过度匹配单纯的值(例如 TagOfInteger
、ClassOfInteger
和 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
,它扩展了Tag[Int]
。因此我们可以安全地推断出T = Int
。在版本 2 中,我们使用
case IntegerTag() =>
。这里IntegerTag
是一个案例类,因此我们知道如果typ
是IntegerTag
的实例,那么这里只能匹配,它扩展标签[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
actuall 是 Tag[String]
而不是 Tag[Int]
的实例.这表明确实存在 case IntegerTag =>
的匹配这一事实并没有告诉我们任何有关 typ
的运行时类型的信息。因此编译器不能假设任何关于 typ
的确切类型:我们只从它声明的静态类型知道它是一个 Tag[T]
但 T
仍然未知。
关于scala - 为什么在这种情况下类型推断会失败?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/15222398/