泛型的Scala模式匹配

ped*_*nto 5 generics scala pattern-matching

我有一个“字符串”列表(类String的包装,名为Str),其中一些具有混合特征。在某个时间点上,我需要区分mixin特性以提供其他功能。

我的代码可以恢复为此,并且可以正常工作:

case class Str(s: String)
trait A
trait B

object GenericsPatternMatch extends {
  def main(args: Array[String]): Unit = {

    val listOfStr: Seq[Str] =
      Seq(
        Str("String"),
        new Str("String A") with A, // Some trait mixins
        new Str("String B") with B
      )

    println("A: " + selectStrA(listOfStr))
    println("B: " + selectStrB(listOfStr))
  }

  val selectStrA: Seq[Str] => Seq[Str with A] = (strList: Seq[Str]) => strList.collect { case s: A => s }
  val selectStrB: Seq[Str] => Seq[Str with B] = (strList: Seq[Str]) => strList.collect { case s: B => s }
}
Run Code Online (Sandbox Code Playgroud)

为了使代码符合DRY原则,我想泛化selectStr函数。我的第一次尝试是:

def selectStrType[T](strList: Seq[Str]): Seq[Str with T] =
    strList.collect { case f: Str with T => f }
Run Code Online (Sandbox Code Playgroud)

但是,由于JVM运行时类型擦除功能(限制?),编译器会发出警告,并且它不起作用,很可能是因为它将与Object的所有内容匹配:

Warning:(31, 31) abstract type pattern T is unchecked since it is eliminated by erasure
    strList.collect { case f: Str with T => f }
Run Code Online (Sandbox Code Playgroud)

经过几个小时的搜索和学习,我想到了:

def selectStrType[T: ClassTag](strList: Seq[Str]): Seq[Str with T] =
    strList.collect {
      case f: Str if classTag[T].runtimeClass.isInstance(f) => f.asInstanceOf[Str with T]
    }
Run Code Online (Sandbox Code Playgroud)

通过这种方法,我现在可以选择如下特定特征:

val selectStrA: Seq[Str] => Seq[Str with A] = (strList: Seq[Str]) => selectStrType[A](strList: Seq[Str])
val selectStrB: Seq[Str] => Seq[Str with B] = (strList: Seq[Str]) => selectStrType[B](strList: Seq[Str])
Run Code Online (Sandbox Code Playgroud)

我相信可能有一种方法可以改善selectStrType函数,即:

  1. 简化if条件
  2. 删除显式强制转换“ .asInstanceOf [Str T]”,但仍返回Seq [Str T]

你能帮助我吗?

Jas*_*r-M 2

您可以按如下方式定义您的方法,它将起作用。

def selectStrType[T: ClassTag](strList: Seq[Str]): Seq[Str with T] =
  strList.collect { case f: T => f }
Run Code Online (Sandbox Code Playgroud)

由于ClassTag上下文绑定,类型匹配就T可以工作(理想情况Str with T下也应该工作,但这似乎是一个限制)。现在编译器知道fhas typeStr和 type T,或者换句话说Str with T,因此可以编译。它会做正确的事情:

scala> selectStrType[A](listOfStr)
res3: Seq[Str with A] = List(Str(String A))

scala> selectStrType[B](listOfStr)
res4: Seq[Str with B] = List(Str(String B))
Run Code Online (Sandbox Code Playgroud)

编辑:更正,这似乎从 Scala 2.13开始工作。在 2.12 中你需要为编译器提供一些帮助:

def selectStrType[T: ClassTag](strList: Seq[Str]): Seq[Str with T] =
  strList.collect { case f: T => f: Str with T }
Run Code Online (Sandbox Code Playgroud)