我想为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
,它使用了类型标签。这似乎很清楚,但再次明显违反合同。它只能在编译时检查静态类型,并且对运行时的实际类型没有任何了解。
所以,我培养了这两种方法并产生了第三种方法,至少可以产生正确的输出。但它看起来很糟糕,并且需要付出很大代价的反射能力。
是否有可能以更优雅的方式解决问题?
答案 0 :(得分:2)
结帐scala.reflect.ClassTag
。它提供对已擦除类Type的访问,并为类型为
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,但它看起来是一个不太冗长的实用解决方案。