这是this question的后续行动。
我的ADT模型如下:
sealed trait Foo {
type A
def method: A
}
case class Bar(p: T, q: S) extends Foo {
type A = Array[String]
def method: A = // implementation
}
case class Baz(p: T, q: S) extends Foo {
type A = Map[Array[String], Array[String]]
def method: A = // implementation
}
我还有另一个类,它将Foo
的子类型作为其构造函数参数之一,并根据它是Bar
还是Baz
来执行特定的操作。
在链接问题的接受答案中,用户@marios向我建议实现这样的想法:
class Qux[X <: Foo](foo: X) {
val m: X#A = foo.method
def process = m match {
case a: Bar#A => // do stuff with Bar
case b: Baz#A => // do other stuff with Baz
}
这很好用,但是由于类型擦除导致了关于未检查类型的警告(因为我在Map
上匹配Baz
)。但在我的特殊情况下,这些可以被安全地忽略。但我想知道我是否可以避免这种情况,并试图写下这样的东西:
class Qux[X <: Foo](foo: X) {
def process(param: U) = {
val m: X#A = foo.method
foo match {
case Bar(_, _) => BarProcessor(m).doStuff(param) // where BarProcessor expects m to be an Array[String]
case Baz(_, _) => BazProcessor(m.keys).doStuff(param, m.values) // do related stuff using m, but where m is instead now a Map[Array[String], Array[String]]
}
}
}
然而,在这种情况下,我得到像
这样的错误[error] Qux.scala:4: type mismatch;
[error] found : X#A
[error] required: Array[String]
[error] case Bar(_, _) => BarProcessor(m).doStuff(rows)
[error] ^
我的问题是,为什么?看起来这两个代码片段实际上并没有什么不同,那么为什么访问类型成员在第一种情况下工作,而不是第二种?
答案 0 :(得分:2)
编译器甚至不根据分支优化foo
的类型(即在分支内部,X
的类型仍为Bar
,而不是Baz
或{ {1}}),更少m
。你可以写的是
def process(param: U) = {
foo match {
case foo: Bar =>
val m = foo.method // m is Bar#A, i.e. Array[String]
BarProcessor(m).doStuff(param)
case foo: Baz =>
val m = foo.method // m is Baz#A, i.e. Map[Array[String], Array[String]]
BazProcessor(m.keys).doStuff(param, m.values)
}
}
请注意,模式中的foo
实际上是一个新变量,它会遮蔽外部foo
,因此这相当于
foo match {
case bar: Bar =>
val m = bar.method // m is Bar#A, i.e. Array[String]
// note that if you use foo here, it still has type Foo
BarProcessor(m).doStuff(param)
...