密封类的清单实例上的模式匹配

时间:2013-05-18 23:22:59

标签: scala pattern-matching

给定课程

sealed abstract class A

case class B(param: String) extends A

case class C(param: Int) extends A

trait Z {}

class Z1 extends Z {}

class Z2 extends Z {}

def zFor[T <: A : Manifest]: Option[Z] = {
  val z = manifest[T].erasure
  if (z == classOf[B]) {
    Some(new Z1)
  } else
  if (z == classOf[C]) {
    Some(new Z2)
  } else {
    None
  }
}

我认为模式匹配的问题是不可能在字节码中构建模式匹配表。这个问题有解决方法吗?可能是我可以使用编译器在Manifest中生成的一些Int吗?

2 个答案:

答案 0 :(得分:2)

如果您有更复杂的类层次结构,那么您编写它的方式就不是很强大,因为如果您有一个类D <: C,那么classOf[D] != classOf[C]。所以你真的不想模式匹配。但你可以;你不能在模式匹配中调用classOf[X],但你可以

def zFor[T <: A : Manifest]: Option[Z] = {
  val ClassB = classOf[B]
  val ClassC = classOf[C]
  manifest[T].erasure match {
    case ClassB => Some(new Z1)
    case ClassC => Some(new Z2)
    case _      => None
  }
}

只要您确定自己处于只需要检测类层次结构叶子的情况。 (您应该通过标记BC final来确保。)

或者,您可以使用isAssignableFrom执行运行时测试:

def zFor2[T <: A : Manifest]: Option[Z] = {
  manifest[T].erasure match {
    case x if classOf[B].isAssignableFrom(x) => Some(new Z1)
    case x if classOf[C].isAssignableFrom(x) => Some(new Z2)
    case _                                   => None
  }
}

现在

class D extends C(5) {}

scala> zFor[D]
res5: Option[Z] = None

scala> zFor2[D]
res6: Option[Z] = Some(Z2@2556af33)

答案 1 :(得分:1)

我不确定这是否适合你的问题(因为你可能已经展示了一个简化的例子)。但是可以在不使用反射的情况下创建这种功能。

这是一种非常常见的模式,允许您在不修改原始代码的情况下创建添加更多组合。

// the - here allows D to return the instance for C
// if you only want exact matches, remove the -
trait GetZ[-X] {
  type Out
  def z: Option[Out]
}

trait LowerPriority {
  implicit def noZ[A] =
    new GetZ[A] {
      type Out = Nothing
      val z = None
    }
}

object GetZ extends LowerPriority {
  implicit def forB =
    new GetZ[B] {
      type Out = Z1
      def z = Some(new Z1)
    }

  implicit def forC =
    new GetZ[C] {
      type Out = Z2
      def z = Some(new Z2)
    }
}

def zFor[T](implicit getZ: GetZ[T]): Option[getZ.Out] = getZ.z

用法

class D extends C(5)

val noZ = zFor[A]
val Some(z1) = zFor[B]
val Some(z2) = zFor[C]
val Some(other_z2) = zFor[D]