我认为,我的问题最好通过一些示例代码来描述:
class Foo[T]
class Bar extends Foo[String]
class Baz extends Foo[Int]
trait X { def f: Foo[_] }
case class Wrapper[D](f: Foo[D]) extends X
val w: X = Wrapper(new Bar)
w match { case Wrapper(_: Bar) => 1 }
最后一行以
失败 found : Bar
required: Foo[Any]
Note: String <: Any (and Bar <: Foo[String]), but class Foo is invariant in type T.
You may wish to define T as +T instead. (SLS 4.5)
我理解这是因为unapply
是使用类型参数定义的,该参数被推断为Any
,因此它抱怨String
不兼容。
但问题是,是否有任何方法可以使其发挥作用?我尝试给exctractor一个类型参数,如:w match { case Wrapper[String](_: Bar) => 1 }
,但是它说它不带参数(这是谎言)...... :(
到目前为止,我想出的唯一方法是这个丑陋的宝贝:
w match { case w: Wrapper[String] if w.f.isInstanceOf[Bar] => 1 }
或者,也许,
Option(w).map(_.f) match { case Some(_: Bar) => 1 }
(后者有效,因为Option
是协变的,但不幸的是我不能使我的类协变)。另外,如果没有额外的丑陋IRL,我真的不能使用最后一种选择,因为在现实生活中相当于X
实际上并没有f
。
有更好的想法吗?
答案 0 :(得分:2)
Wrapper.unapply
确实采用了type参数,但是你不能在模式匹配序列中指定一个*,因此编译器会为你推断一个*(如果编译器这样做,那么它常常是Any
或Nothing
)。
实际上,您并不想要它,因为当您强迫元素键入X
时,您将删除类型信息。所以,你想要一个存在版本的匹配器
object WrapperEx {
def unapply(w: Wrapper[_]): Option[Foo[_]] = Wrapper.unapply(w)
}
并像这样使用它:
w match { case WrapperEx(_: Bar) => 1 }
Runnable version here
尽管如此,我还是说它不是半坏的
*你可以使用最新的Typelevel Scala,但我不确定它是如何用于类型转换的,我无法让它适用于你的情况。
答案 1 :(得分:1)
您可以参数化trait X
以摆脱def f: Foo[_]
中的存在类型,我认为这是编译器的绊倒。以下代码有效:
class Foo[T]
class Bar extends Foo[String]
class Baz extends Foo[Int]
trait X[A] { def f: Foo[A] }
case class Wrapper[D](f: Foo[D]) extends X[D]
val w: X[String] = Wrapper(new Bar) // type ascription can be omitted and will be inferred
w match { case Wrapper(_: Bar) => 1 }