Scala通用性与trait匹配

13 浏览
0 Comments

Scala通用性与trait匹配

看这段代码。

trait SomeMix {

}

trait Processor[T] {

def processMix(t: T with SomeMix) = {

println("处理带有Mix的T")

}

def processAsUsual(t:T)= {

println("处理T")

}

def process(t:T) = {

t match {

case mix: SomeMix => processMix(mix) // <---- 这里有错误

case _ => processAsUsual(t)

}

}

}

愚蠢的Scala编译器在这里显示错误:

错误:(22, 39)类型不匹配;

找到:mix.type(具有底层类型SomeMix)

需要:T with SomeMix

case mix: SomeMix => processMix(mix)

它不明白我匹配到的SomeMix表达式已经是类型T了。好吧,让我们帮助他。修改代码:

def process(t:T) = {

t match {

case mix: T with SomeMix => processMix(mix) // <---- 这里有警告

case _ => processAsUsual(t)

}

}

现在它同意一切都正确了,但是显示警告:

警告:(22, 17)抽象类型模式T未经检查,因为它被擦除

case mix: T with SomeMix => processMix(mix)

有没有好的方法可以避免这里的错误和警告?

0
0 Comments

问题的出现原因是在trait Processor中的processMix方法只能处理T类型的参数,而不能处理SomeMix类型的参数。实际上,processMix方法调用了一些需要T和SomeMix功能的方法。

解决方法是将processMix方法的参数类型改为SomeMix,并在方法内部进行类型转换。代码如下:

trait Processor[T] {
  def processMix(mix: SomeMix) = {
    val t = mix.asInstanceOf[T]
    println("processing SomeMix")
    // 进行一些需要T和SomeMix功能的操作
  }
  def processAsUsual(t:T)= {
    println("processing T")
  }
  def process(t:T) = {
    t match {
      case mix: SomeMix => processMix(mix)
      case _ => processAsUsual(t)
    }
  }
}

0
0 Comments

Scala泛型与特质匹配的问题出现是因为类型擦除导致无法检查t是否是T with SomeMix的实例。因此,我们需要使用静态分派的类型类(typeclasses)来替代动态类型分派。

举个例子:

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)

感谢这个例子,它给了我一些新的想法。但是作为解决这个特定问题的方法并不好。处理的逻辑被移到了trait Process之外,这是不可接受的。但是也许我可以稍微修改一下以适应我的情况。

你可以将隐式参数processString和processStringWithSomeMix放到object Process中。在Scala中,将隐式参数的定义放在类型类的伴生对象中是一种常用的模式,这并不是将逻辑移到外部。

0
0 Comments

问题出现的原因是在匹配泛型类型时,出现了未检查的警告。解决方法是使用case mix: (T ) with SomeMix来匹配类型,并将未检查的警告关闭。需要注意的是,这种匹配在运行时只会检查被匹配的对象是否是SomeMix的实例,如果改成def process(t: Any) = ...将会得到错误的结果。虽然匹配泛型类型是一个不好的主意,但在某些情况下可能会出现这种情况。

0