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

What is the proper way to wrap isInstanceOf[] calls?

我想为 isInstanceOf[T]asInstanceOf[T] 对构建一个包装器,使用方便的 mapgetOrElse 方法输出 Option[T]

所以我试了一下,结果让我很失望

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 看起来不错,但就是行不通,它删除了 isInstanceOf[U] 方法应用程序中的 U 类型,因此它总是以 true 响应。有趣的是 asInstanceOf[U] 保持 U 类型正常,所以它只是产生运行时异常。

我尝试的第二种方法是 WrapFullTagged,它使用类型标签。看起来很清楚,但又明显违约了。它只能在编译时检查静态类型,对运行时的实际类型了解为零。

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

是否可以更优雅地解决问题?

查看 scala.reflect.ClassTag。它提供对已擦除 class 类型的访问,并为类型为

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

A ClassTag[T] can serve as an extractor that matches only objects of type 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)

综上所述;虽然这使用了反射 api,但它看起来是一个不太冗长的实用解决方案。