当成员在该类型上协变时,基于包含的类型提取集合的成员

时间:2015-04-03 03:01:49

标签: scala covariance

我有一个关于其成员变量之一的类型的案例类协变,它被约束为特定类型:

case class MyCount[+T <: Identifier](
  id: T,
  count: Long,
)

标识符是一个密封的特性,有两个具体的实现,但我不认为这对问题很重要:

sealed trait Identifier
case class IdentifierFoo(...) implements Identifier
case class IdentifierBar(...) implements Identifier

鉴于我想要的MyCount[Identifier]集合:

  1. 提取所有MyCount[IdentifierFoo]
  2. 让生成的集合具有SomeCollection[MyCount[IdentifierFoo]]
  3. 类型

    显而易见的(对我来说)这样做的方式如下:

    src.collect { case countFoo: MyCount[IdentifierFoo] => countFoo }
    

    但是,这会失败,因为在运行时由于类型擦除而无法准确检查Count的类型:结果(错误地)获取所有Count s。我最终做了一些非常毛茸茸的事情:

    src.collect { count => 
      count.id match { case IdentifierFoo => { 
        count match {case countFoo: MyCount[IdentifierFoo] => countFoo }
    } } }
    

    这很有效,但很难看。我也尝试按如下方式匹配整个计数:

    src.collect { case countFoo: MyCount[IdentifierFoo](_: IdentifierFoo, _) => countFoo }
    

    ...但这似乎在Scala 2.10中无效,这是我受限制的。有没有更好的方法来做我想做的事情?

2 个答案:

答案 0 :(得分:1)

由于您拥有IdentifierFooIdentifierBar的提取器并且知道它们的结构,因此您可以使用它们。通过使用提取器,您不仅仅匹配类型,因此我们绕过类型擦除。

list.collect { case countFoo @ MyCount(IdentifierFoo(_), _) => countFoo }

例如:

sealed trait Identifier
case class IdentifierFoo(id: Int) extends Identifier
case class IdentifierBar(id: Int) extends Identifier
case class MyCount[+T <: Identifier](id: T, count: Long)
val list = List(MyCount(IdentifierFoo(1), 2), MyCount(IdentifierBar(2), 3), MyCount(IdentifierBar(3), 4))

scala> list.collect { case countFoo @ MyCount(IdentifierFoo(_), _) => countFoo }
res142: List[MyCount[Product with Serializable with Identifier]] = List(MyCount(IdentifierFoo(1),2))

scala> list.collect { case countFoo @ MyCount(IdentifierBar(_), _) => countFoo }
res143: List[MyCount[Product with Serializable with Identifier]] = List(MyCount(IdentifierBar(2),3), MyCount(IdentifierBar(3),4))

答案 1 :(得分:1)

正如@ m-z所提到的,你最好在结构上匹配而不是类型,但是如果你希望结果是List[MyCount[IdentifierFoo]]类型,你必须强制转换值:

val list: List[MyCount[Identifier]] = List(MyCount(IdentifierFoo(1), 2), MyCount(IdentifierBar(2), 3), MyCount(IdentifierBar(3), 4))

 list.collect{ case countFoo @ MyCount(_ : IdentifierFoo,_) => countFoo.asInstanceOf[MyCount[IdentifierFoo]]}  
 res0: List[MyCount[IdentifierFoo]] = List(MyCount(IdentifierFoo(1),2))

 list.collect{ case countFoo @ MyCount(_ : IdentifierBar,_) =>  countFoo.asInstanceOf[MyCount[IdentifierBar]]} 
 res1: List[MyCount[IdentifierBar]] = List(MyCount(IdentifierBar(2),3), MyCount(IdentifierBar(3),4))