为什么类的泛型构造函数参数的模式匹配失败,而方法的泛型参数的模式匹配成功?
我有一个类型类
Ev
:
sealed trait Ev[T]
case object EvInt extends Ev[Int]
case object EvStr extends Ev[String]
和一个像这样的通用类:
class C[T](ev: Ev[T]) {
val id: Int = ev match {
case EvInt => 0
case EvStr => 1
}
}
以上
ev
上的模式匹配失败,并显示:
error: pattern type is incompatible with expected type;
found : EvInt.type
required: Ev[T]
case EvInt => 0
而这有效:
object C {
def id[T](ev: Ev[T]): Int = {
ev match {
case EvInt => 0
case EvStr => 1
}
}
}
出于我的目的,我可以通过多种方式解决这个问题,例如,将
id
移至 Ev
特征中:
sealed trait Ev[T] {
def id: Int
}
class C[T](ev: Ev[T]) { val id: Int = ev.id }
但我很好奇为什么它会失败。
认为它一开始就应该起作用是有道理的。正如一些人评论的那样,scala 2 在 ADT 和类型推断/检查方面存在一些缺陷。 并且您已经知道此问题的解决方法。所以我要在这里写下我的“实验”/假设。
编译器说它需要一个
Ev[T]
,但您为它提供了 EvInt
和 EvString
。但我一开始的想法是,它试图在幕后说它需要 T
,但你为它提供了 Int
和 String
。我认为为什么会发生这种情况,可能是因为类、方法和其他标识符中的类型推断规则不同。例如,如果您将代码更改为:
class C[T](ev: Ev[T]) {
// or use ev.asInstanceOf[Ev[_]]
val id: Int = (ev: Ev[_]) match {
case EvInt => 0
case EvStr => 1
}
}
一切都会好起来的!因为你基本上是在告诉编译器这里没有具体的
T
。
请注意,如果您指定类型,则不会遇到此问题:
ev match {
case _ : EvInt.type => 0
case _ : EvString.type => 1
}
此外,如果您使用
val
,而不是 case 对象(首先扩展自己的类型),编译错误将会消失:
val eInt: Ev[Int] = EvInt
val eStr: Ev[String] = EvString
val id: Int = ev match {
case eInt => 0
case eStr => 1
}
所以总的来说,我认为编译器无法证明
EvInt
和 EvString
与 Ev[*T*]
之间的关系,并且这种类型推断问题在 scala 2 中很常见。我想说这是一个错误,但我对此没有信心。
希望这有帮助!