首页 > 解决方案 > 泛型列表上的模式匹配

问题描述

我有一个类,其中包含一个泛型类型的序列,例如:

sealed trait Interface {}

case class Imp1() extends Interface {}
case class Imp2() extends Interface {}

case class Wrapper[+I <: Interface](interface: I) {}

case class WrapperList(list: Seq[Wrapper[Interface]]) {
    ...
}

WrapperList我希望能够遍历每个类型的包装序列和模式匹配,例如。

def getImp1s(remaining: Seq[Wrapper[Interface]] = list): Seq[Wrapper[Imp1]] = {
  if (remaining.length == 0) Seq()
  else remaining.head match {
    case wrapper: Wrapper[Imp1] => get(remaining.tail) :+ wrapper
    case _                      => get(remaining.tail)
  }
}

你可能会猜到我遇到了

non-variable type argument Playground.Imp1 in type pattern Playground.Wrapper[Playground.Imp1] is unchecked since it is eliminated by erasure

为了克服这个问题,我认为我可以使用TypeTagsClassTags保留类型,例如:

case class Wrapper[+I <: Interface](interface: I)(implicit tag: TypeTag[I]) {}

但是,这似乎不起作用,我仍然收到相同的警告。有人可以解释我如何使用 TypeTag 进行匹配吗?我宁愿避免创建扩展通用抽象类的通用类的具体版本,但请理解这可能是最简单的解决方案。

谢谢你的帮助 :)

标签: scalagenericspattern-matching

解决方案


尝试

import shapeless.TypeCase

val `Wrapper[Imp1]` = TypeCase[Wrapper[Imp1]]

def getImp1s(remaining: Seq[Wrapper[Interface]]): Seq[Wrapper[Imp1]] = {
  if (remaining.isEmpty) Seq()
  else remaining.head match {
    case `Wrapper[Imp1]`(wrapper) => getImp1s(remaining.tail) :+ wrapper
    case _                        => getImp1s(remaining.tail)
  }
}

getImp1s(Seq(Wrapper(Imp1()), Wrapper(Imp2()), Wrapper(new Interface {}))) 
// List(Wrapper(Imp1()))
getImp1s(Seq(Wrapper(Imp2()), Wrapper(Imp1()), Wrapper(new Interface {}))) 
// List(Wrapper(Imp1()))

无需使用自定义提取器的 Shapeless 也可以实现相同的效果

object `Wrapper[Imp1]` {
  def unapply(arg: Any): Option[Wrapper[Imp1]] = arg match {
    case Wrapper(Imp1()) => Some(Wrapper(Imp1()))
    case _               => None
  }
}

或直接

def getImp1s(remaining: Seq[Wrapper[Interface]]): Seq[Wrapper[Imp1]] = {
  if (remaining.isEmpty) Seq()
  else remaining.head match {
    case Wrapper(Imp1()) => getImp1s(remaining.tail) :+ Wrapper(Imp1())
    case _               => getImp1s(remaining.tail)
  }
}

或者

def getImp1s(remaining: Seq[Wrapper[Interface]]): Seq[Wrapper[Imp1]] = {
  if (remaining.isEmpty) Seq()
  else remaining.head match {
    case Wrapper(_: Imp1) => getImp1s(remaining.tail) :+ Wrapper(Imp1())
    case _                => getImp1s(remaining.tail)
  }
}

推荐阅读