Scala шаблон-матч по дженерикам - PullRequest
5 голосов
/ 13 марта 2019

У меня есть список «string» (обертка вокруг класса String с именем Str), некоторые из них со смешанными чертами.В какой-то момент мне нужно различить черты миксина, чтобы обеспечить дополнительные функциональные возможности.

Мой код может быть возобновлен до этого, и он отлично работает:

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, а именно:

  1. Упрощение условия if
  2. Удаление явного приведения ".asInstanceOf [Str with T] ", но все еще возвращаю Seq [Str with T]

Можете ли вы мне помочь?

1 Ответ

2 голосов
/ 14 марта 2019

Вы можете определить свой метод следующим образом, и он будет работать.

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

Из-за ограничения контекста ClassTag совпадение типов будет работать только на T (в идеале 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 }
...