我从 scala 2.10.0-RC1 添加带有 Dynamic
的变量,如下所示:
import language.dynamics
import scala.collection.mutable.HashMap
object Main extends Dynamic {
private val map = new HashMap[String, Any]
def selectDynamic(name: String): Any = {return map(name)}
def updateDynamic(name:String)(value: Any) = {map(name) = value}
}
val fig = new Figure(...) // has a method number
Main.figname = fig
现在,如果我想访问 Main.figname.number
它不起作用,因为编译器认为它是 Any
类型。
但它也是 Main.figname.isInstanceOf[Figure] == true
,所以它是 Any
和 Figure
,但没有数字
能力。现在我可以将其转换为 Main.figname.asInstanceOf[Figure].number
并且它可以工作!这太丑了!我无法将其呈现给我的域用户(我想构建一个内部 DSL。)
注意:如果我使用 Figure
的父类(super class)型而不是 Any
,它也不起作用。
这是 scala 2.10 中的错误还是功能?
最佳答案
这是非常符合逻辑的。您显式返回 Any
的实例。解决方法是始终使用 Dynamic 实例:
import language.dynamics
import scala.collection.mutable.HashMap
import scala.reflect.ClassTag
trait DynamicBase extends Dynamic {
def as[T:ClassTag]: T
def selectDynamic[T](name: String): DynamicBase
def updateDynamic(name:String)(value: Any)
}
class ReflectionDynamic( val self: Any ) extends DynamicBase with Proxy {
def as[T:ClassTag]: T = { implicitly[ClassTag[T]].runtimeClass.asInstanceOf[Class[T]].cast( self ) }
// TODO: cache method lookup for faster access + handle NoSuchMethodError
def selectDynamic[T](name: String): DynamicBase = {
val ref = self.asInstanceOf[AnyRef]
val clazz = ref.getClass
clazz.getMethod(name).invoke( ref ) match {
case dyn: DynamicBase => dyn
case res => new ReflectionDynamic( res )
}
}
def updateDynamic( name: String )( value: Any ) = {
val ref = self.asInstanceOf[AnyRef]
val clazz = ref.getClass
// FIXME: check parameter type, and handle overloads
clazz.getMethods.find(_.getName == name+"_=").foreach{ meth =>
meth.invoke( ref, value.asInstanceOf[AnyRef] )
}
}
}
object Main extends DynamicBase {
def as[T:ClassTag]: T = { implicitly[ClassTag[T]].runtimeClass.asInstanceOf[Class[T]].cast( this ) }
private val map = new HashMap[String, DynamicBase]
def selectDynamic[T](name: String): DynamicBase = { map(name) }
def updateDynamic(name:String)(value: Any) = {
val dyn = value match {
case dyn: DynamicBase => dyn
case _ => new ReflectionDynamic( value )
}
map(name) = dyn
}
}
用法:
scala> class Figure {
| val bla: String = "BLA"
| }
defined class Figure
scala> val fig = new Figure() // has a method number
fig: Figure = Figure@6d1fa2
scala> Main.figname = fig
Main.figname: DynamicBase = Figure@6d1fa2
scala> Main.figname.bla
res40: DynamicBase = BLA
所有实例都包装在动态实例中。
我们可以使用执行动态转换的 as
方法恢复实际类型。
scala> val myString: String = Main.figname.bla.as[String]
myString: String = BLA
关于scala - 避免对 scala 的动态类型进行显式强制转换,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/13253557/