包装 isInstanceOf[] 调用的正确方法是什么?
What is the proper way to wrap isInstanceOf[] calls?
我想为 isInstanceOf[T]
和 asInstanceOf[T]
对构建一个包装器,使用方便的 map
和 getOrElse
方法输出 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,但它看起来是一个不太冗长的实用解决方案。
我想为 isInstanceOf[T]
和 asInstanceOf[T]
对构建一个包装器,使用方便的 map
和 getOrElse
方法输出 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 类型的访问,并为类型为
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,但它看起来是一个不太冗长的实用解决方案。