当成员在该类型上协变时,基于包含的类型提取集合的成员
Extracting members of a collection based on the contained type when the member is covariant on that type
我有一个案例 class 在其成员变量之一的类型上是协变的,它被限制为特定类型:
case class MyCount[+T <: Identifier](
id: T,
count: Long,
)
标识符是具有两个具体实现的密封特征,但我认为这对问题不重要:
sealed trait Identifier
case class IdentifierFoo(...) implements Identifier
case class IdentifierBar(...) implements Identifier
鉴于 MyCount[Identifier]
的集合,我想:
- 提取全部
MyCount[IdentifierFoo]
- 生成的集合具有类型
SomeCollection[MyCount[IdentifierFoo]]
(对我而言)显而易见的做法如下:
src.collect { case countFoo: MyCount[IdentifierFoo] => countFoo }
但是,这失败了,因为由于类型擦除,无法在运行时准确检查 Count
的类型:结果(错误地)得到所有 Count
。我最终做了一些看起来很毛茸茸的事情:
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 中似乎无效,这是我所受的限制。有没有更好的方法来做我想做的事?
因为您有 IdentifierFoo
和 IdentifierBar
的提取器并且知道它们的结构,所以您可以使用它们。通过使用提取器,您不仅仅在类型上进行匹配,因此我们绕过了类型擦除。
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))
正如@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))
我有一个案例 class 在其成员变量之一的类型上是协变的,它被限制为特定类型:
case class MyCount[+T <: Identifier](
id: T,
count: Long,
)
标识符是具有两个具体实现的密封特征,但我认为这对问题不重要:
sealed trait Identifier
case class IdentifierFoo(...) implements Identifier
case class IdentifierBar(...) implements Identifier
鉴于 MyCount[Identifier]
的集合,我想:
- 提取全部
MyCount[IdentifierFoo]
- 生成的集合具有类型
SomeCollection[MyCount[IdentifierFoo]]
(对我而言)显而易见的做法如下:
src.collect { case countFoo: MyCount[IdentifierFoo] => countFoo }
但是,这失败了,因为由于类型擦除,无法在运行时准确检查 Count
的类型:结果(错误地)得到所有 Count
。我最终做了一些看起来很毛茸茸的事情:
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 中似乎无效,这是我所受的限制。有没有更好的方法来做我想做的事?
因为您有 IdentifierFoo
和 IdentifierBar
的提取器并且知道它们的结构,所以您可以使用它们。通过使用提取器,您不仅仅在类型上进行匹配,因此我们绕过了类型擦除。
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))
正如@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))