Scala 泛型的模式匹配

标签 scala generics 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 }
}

为了保持代码符合 DRY 原则,我想生成 selectStr 函数。
我的第一次尝试是:
def selectStrType[T](strList: Seq[Str]): Seq[Str with T] =
    strList.collect { case f: Str with T => f }

然而,由于 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 }

经过几个小时的搜索和学习,我想出了:
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]
    }

使用这种方法,我现在可以选择这样的特定特征:
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])

我相信可能有一种方法可以改进 selectStrType 函数,即:
  • 简化 if 条件
  • 删除显式转换“.asInstanceOf[Str with T]”,但仍返回 Seq[Str with T]

  • 你能帮助我吗?

    最佳答案

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

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

    因为 ClassTag上下文仅在 T 上绑定(bind)类型匹配将起作用(理想情况下 Str with T 也应该起作用,但这似乎是一个限制)。现在编译器知道 f有类型 Str并输入 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))
    

    编辑:更正,这似乎可行从 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 }
    

    关于Scala 泛型的模式匹配,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/55146472/

    相关文章:

    Scala编译错误

    scala - Scala 中 int 的 += 方法在哪里

    Java 正则表达式非常慢(将嵌套量词转换为所有格量词)

    scala - 如何在 Playframework 中使用 Oracle 存储过程和 Scala Anorm

    scala - 高阶函数定义中的括号错误(Scala)

    generics - 为什么在具有上限的投影泛型类型中,生产者方法的返回类型为Any?而不是上限类型?

    go - 在 Go 泛型(版本 1.18)或更高版本中使用 "void"类型作为参数化类型

    Java:T对象; obj.getClass() 的类型是 Class<?> 而不是 Class<?延伸 T>。为什么?

    javascript - 提取两个模式之间的数字javascript

    regex - 模式匹配 Haskell 中的正则表达式模式