具有特征匹配的 Scala 泛型

Scala generic with trait matching

看看这段代码。

trait SomeMix {

}

trait Processor[T] {

  def processMix(t: T with SomeMix) = {
    println("processing T with Mix")
  }

  def processAsUsual(t:T)= {
    println("processing T")
  }

  def process(t:T) = {
    t match {
      case mix: SomeMix => processMix(mix) // <---- error here 
      case _ => processAsUsual(t)
    }
  }
}

愚蠢的 Scala 编译器在此处显示错误:

错误:(22, 39) 类型不匹配; 发现:mix.type(底层类型为 SomeMix) 要求:T 与 SomeMix 案例组合:SomeMix => processMix(mix)

不明白匹配SomeMix的表达式I已经是T类型了,好吧,帮帮他吧。更改代码:

   def process(t:T) = {
    t match {
      case mix: T with SomeMix => processMix(mix) // <---- warning here 
      case _ => processAsUsual(t)
    }
  }

现在同意一切正确但显示警告:

警告:(22, 17) 抽象类型模式 T 未选中,因为它已被擦除消除 案例组合:T with SomeMix => processMix(mix)

这里有什么既可以避免错误又可以警告的好方法吗?

像这样?

trait SomeMix {

}

trait Processor[T] {

  def processMix(t: SomeMix) = {
    println("processing SomeMix")
  }

  def processAsUsual(t:T)= {
    println("processing T")
  }

  def process(t:T) = {
    t match {
      case mix: SomeMix => processMix(mix)
      case _ => processAsUsual(t)
    }
  }
}

Scala编译器不傻。由于类型擦除,您无法检查 t 是 T with SomeMix 的实例。尝试使用带有静态调度的类型类而不是动态类型调度。

例如

trait SomeMix {
  def someMethod: String = "test2"
}

class SomeType

def process[T](t: T)(implicit P: Process[T]): Unit = P.process(t)

trait Process[T] {
  def process(t: T): Unit
}

implicit val processString: Process[SomeType] = s =>
  println(s"processing $s as usual")
implicit val processStringWithSomeMix: Process[SomeType with SomeMix] = s =>
  println(s"processing $s with mix ${s.someMethod}")

process(new SomeType)
process(new SomeType with SomeMix)

您可以像@ppressives 建议的那样在编译时执行此操作。 如果您真的想在运行时执行此操作,您应该找到一种在编译后保留类型的方法。在 Scala 中,执行此操作的标准方法是 TypeTags.

尝试

import reflect.runtime.universe.{TypeTag, typeOf}

def typ[A: TypeTag](a: A) = typeOf[A]

def process(t: T)(implicit ev: TypeTag[T with SomeMix], ev1: TypeTag[T]) = {
  t match {
    case mix if typ(t) <:< typeOf[T with SomeMix] => processMix(mix.asInstanceOf[T with SomeMix])
    case _ => processAsUsual(t)
  }
}

val p = new Processor[Int] {}
p.process(10) //processing T

val p1 = new Processor[Int with SomeMix] {}
val ten = 10.asInstanceOf[Int with SomeMix]
p1.process(ten) //processing T with Mix

检查

Pattern match of scala list with generics

Pattern matching on generic type in Scala

因为,正如您提到的,它绝对是 T 的一个实例,您可以只抑制未经检查的警告:

case mix: (T @unchecked) with SomeMix

请注意,它仍未选中,并且在运行时仅测试匹配项是 SomeMix 的实例;如果您更改为例如

def process(t: Any) = ...

你会得到不好的结果。