在Scala中使用TypeTag在类型层次结构上进行模式匹配的惯用方法是什么?

时间:2014-10-16 15:53:50

标签: scala reflection pattern-matching scala-2.10

我有一个类型层次结构,想要查询实现的查找方法。我没有诉诸asInstanceOf电话而无法做到这一点。

所以给出一个简单的类型层次结构

trait Vehicle

trait Flying extends Vehicle
class Plane extends Flying

trait Driving extends Vehicle
class Car extends Driving

trait Swimming extends Vehicle
class Boat extends Swimming

我的查找方法就像这样

def findVehicle[V <: Vehicle](implicit tag: TypeTag[V]): Option[V] = {

  val v = tag.tpe match {
    case t if t =:= typeOf[Flying] => Some(new Plane)
    case t if t =:= typeOf[Driving] => Some(new Car)
    case t if t =:= typeOf[Swimming] => Some(new Boat)
    case _ => None
  }

  v.map(_.asInstanceOf[V])
}

使用类似的查找

println(findVehicle[Flying])  // Some(Plane@5b7fd935)

是否可以在没有asInstanceOf的情况下实现这样的查找?

1 个答案:

答案 0 :(得分:2)

您可以避免使用TypeTag并改为使用类型类。

trait Builder[+T] {
  def build: Option[T]
}

implicit val flyingBuilder = new Builder[Flying] {
  def build = Some(new Plane)
}

implicit val drivingBuilder = new Builder[Driving] {
  def build = Some(new Car)
}

implicit val swimmingBuilder = new Builder[Swimming] {
  def build = Some(new Boat)
}

implicit val anyBuilder = new Builder[Nothing] {
  def build = None
}

def findVehicle[V <: Vehicle](implicit b: Builder[V]): Option[V] = b.build

没有涉及反思,这肯定更具惯用性。

请注意,定义Builder[Nothing]如何通过返回None来重现您所获得的相同行为。这不一定是个好主意,因为你现在被迫检查该方法是否能够产生一个值。

如果无法构建所需类型的实例,我宁愿选择编译时错误,而是通过直接从T返回Option[T]而不是build来实现它。 (当然要摆脱Nothing案例)。