scala - 处理具有不变类型参数的案例类的提取器

标签 scala match type-parameter

我认为,我的问题最好用一些示例代码来描述:

 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不相容。

但问题是有什么方法可以让它发挥作用吗?我试着给提取器一个类型参数,像这样: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

有什么更好的主意吗?

最佳答案

定义自定义提取器

Wrapper.unapply 确实采用类型参数,但您不能在模式匹配序列中指定一个*,因此编译器会为您推断一个(如果编译器这样做,通常是 Any Nothing)。

而且,实际上,您不希望它这样做,因为当您强制元素键入 X 时,您正在删除类型信息。所以,你想要一个存在版本的匹配器

object WrapperEx {
  def unapply(w: Wrapper[_]): Option[Foo[_]] = Wrapper.unapply(w)
}

然后像这样使用它:

w match { case WrapperEx(_: Bar) => 1 }

可运行版本 here

  • 好消息:您可以委托(delegate)给生成的案例类匹配器。
  • 坏消息:您不能在案例类同伴中定义它。 Scala 很高兴已经选错了,所以它无法消除歧义。

不过,我会说它还不错


* 你可以在最新的 Typelevel Scala 中使用,但我不确定它如何与类型转换一起工作,我无法让它为你的情况工作。

关于scala - 处理具有不变类型参数的案例类的提取器,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/45881356/

相关文章:

java - 有没有办法获得SQS队列的平均服务时间?

c# - 使用正则表达式从 AssemblyInfo.cs 文件中检索程序集版本

generics - 为什么我不能在带有类型参数的特征上添加一揽子实现?

Scala trait 及其方法的参数化

scala - intellij - 我要创建哪个 Scala 项目,Scala-Scala 还是 Scala-SBT?

scala - Spark Streaming DStream RDD 获取文件名

java - Scala 可以独立运行(无需先安装 Java)吗?安装哪个 Java 有关系吗?

javascript - 如何让 RegEx 获取整个 URL...从 http 开始并获取之后的所有内容,直到空格以及如何排除某些字符

php - 如何对两个输入字符串进行文件检查?