包装isInstanceOf []调用的正确方法是什么?

时间:2016-09-19 19:57:22

标签: scala reflection dynamic-cast

我想为isInstanceOf[T]asInstanceOf[T]对构建一个包装器,使用方便的Option[T]map方法输出getOrElse。< / p>

所以我试一试,但结果让我很失望。

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

class Base()
class Deep() extends Base
class Deeper() extends Deep()

final case class WrapSimple[T](source : T) {
  def cast[U] : Option[U] =
    if (source.isInstanceOf[U]) Some(source.asInstanceOf[U]) else None
}

final case class WrapFullTagged[T: TypeTag](source : T) {
  def cast[U : TypeTag] : Option[U] =
    if (typeOf[T] <:< typeOf[U]) Some(source.asInstanceOf[U]) else None
}

final case class WrapHalfTagged[T](source : T) {
  val stpe = {
    val clazz = source.getClass
    val mirror = scala.reflect.runtime.universe.runtimeMirror(clazz.getClassLoader)
    mirror.classSymbol(clazz).toType
  }
  def cast[U : TypeTag] : Option[U] =
    if (stpe <:< typeOf[U]) Some(source.asInstanceOf[U]) else None
}

object Test {
  val base = new Base
  val deep = new Deep
  val deeper = new Deeper
  val wile : Deep = new Deeper

  def testSimple() : Unit = {
    println(WrapSimple(deep).cast[Base].isDefined) // should be true
    println(WrapSimple(deep).cast[Deeper].isDefined) // should be false
    println(WrapSimple(wile).cast[Deeper].isDefined) // should be true
  }

  def testFullTagged() : Unit = {
    println(WrapFullTagged(deep).cast[Base].isDefined) // should be true
    println(WrapFullTagged(deep).cast[Deeper].isDefined) // should be false
    println(WrapFullTagged(wile).cast[Deeper].isDefined) // should be true
  }

  def testHalfTagged() : Unit = {
    println(WrapHalfTagged(deep).cast[Base].isDefined) // should be true
    println(WrapHalfTagged(deep).cast[Deeper].isDefined) // should be false
    println(WrapHalfTagged(wile).cast[Deeper].isDefined) // should be true
  }

  def testAll() : Unit = {
    testSimple()
    testFullTagged()
    testHalfTagged()
  }
}

WrapSimple看起来不错,但只是不起作用,它会删除U方法应用中的isInstanceOf[U]类型,因此始终以true响应。有趣的是asInstanceOf[U]正常保留U类型,因此它只会产生运行时异常。

我尝试过的第二种方法是WrapFullTagged,它使用了类型标签。这似乎很清楚,但再次明显违反合同。它只能在编译时检查静态类型,并且对运行时的实际类型没有任何了解。

所以,我培养了这两种方法并产生了第三种方法,至少可以产生正确的输出。但它看起来很糟糕,并且需要付出很大代价的反射能力。

是否有可能以更优雅的方式解决问题?

1 个答案:

答案 0 :(得分:2)

结帐scala.reflect.ClassTag。它提供对已擦除类Type的访问,并为类型为

的函数提供according to the api docs
def unapply(x: Any): Option[T]
  

ClassTag [T]可以作为只匹配T类型对象的提取器。

与问题中的预期输出相匹配并显得相当优雅的示例:

class Base()
class Deep() extends Base
class Deeper() extends Deep()

case object SimpleCaster {
  def cast[A](t: Any)(implicit classTag: scala.reflect.ClassTag[A]): Option[A] = classTag.unapply(t)
}

object Test {
  val base = new Base
  val deep = new Deep
  val deeper = new Deeper
  val wile: Deep = new Deeper

  def testSimple(): Unit = {
    val a = SimpleCaster.cast[Base](deep)
    val b = SimpleCaster.cast[Deeper](deep)
    val c = SimpleCaster.cast[Deeper](wile)
    println(s"${a.isDefined} - ${a.map(_.getClass)}")
    println(s"${b.isDefined} - ${b.map(_.getClass)}")
    println(s"${c.isDefined} - ${c.map(_.getClass)}")
  }
}

导致控制台输出:

scala> Test.testSimple
true - Some(class Deep)
false - None
true - Some(class Deeper)

总结;虽然这使用反射apis,但它看起来是一个不太冗长的实用解决方案。