Scala如何将模式匹配与继承和模板化类一起使用

时间:2020-07-29 10:08:54

标签: scala reflection scala-reflect

我们在公司使用的数据结构如下:

trait Resource

case class templatedResource[T](t: T) extends Resource

case class BParameter()
case class CParameter()

object B {
  type Resource = templatedResource[BParameter]
}

object C {
  type Resource = templatedResource[CParameter]
}

在某个时候,给定一些未知的Resource,我们希望使用模式匹配来确定其内部类型并启动一些不同的处理。

但是由于类型擦除,简单的模式匹配不起作用。因此,我们尝试使用TypeTags,但没有成功:

import scala.reflect.runtime.universe._

object Service {

  def process(resource: Resource)(implicit tag: WeakTypeTag[Resource]) = {
    (tag.tpe, resource) match {
      case (t, b: B.Resource) if t =:= typeOf[B.Resource] =>
        println("b !!")
      case (t, c: C.Resource) if t =:= typeOf[C.Resource] => 
        println("c !!")
      case _ => 
          throw new IllegalStateException(s"Unexpected resource type")
    }
  }
}

val bParam = BParameter()
val bResource: B.Resource = templatedResource(bParam)

Service.process(bResource)
//  throws java.lang.IllegalStateException: Unexpected resource type
//         at Service$.process(<console>:26)

似乎t =:= typeOf[B.Resource]总是错误的,因为t只知道Resource特质(t =:= typeOf[Resource]而不是具体实现。

如何使这种模式匹配起作用?

1 个答案:

答案 0 :(得分:1)

您应该将擦除类型参数固定为某种新类型。类型别名不是新类型,只是当前类型的附加名称。

您可以执行以下操作:

trait Resource

class templatedResource[T](t: T) extends Resource

case class BParameter()
case class CParameter()

object B {
  case class Resource(val b: BParameter) extends templatedResource[BParameter](b)
}

object C {
  type Resource = templatedResource[CParameter]
}

def process(r: Resource) = {
  r match {
    case a: B.Resource => true
  }
}

process(B.Resource(BParameter()))

如果您需要保留创建语法val bResource: B.Resource = templatedResource(bParam)以消除最终用户的样板,则应使用此类创建来定义函数。消除实施 该功能的样板-我猜您可以使用宏或类似无形的东西。