我在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
,但没有Figures
个能力。现在我可以像Main.figname.asInstanceOf[Figure].number
一样投射它并且它有效!这太丑了!而且我无法向我的域用户呈现此内容(我想构建一个内部DSL。)
注意:如果我使用Any
代替Figure
,那么它也不起作用。
这是scala 2.10中的错误还是功能?
答案 0 :(得分:2)
这很合乎逻辑。您显式返回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
所有实例都包含在Dynamic实例中。
我们可以使用执行动态转换的as
方法恢复实际类型。
scala> val myString: String = Main.figname.bla.as[String]
myString: String = BLA
答案 1 :(得分:0)
您可以向Any
或任何预定义的value classes
添加任何扩展程序或自定义功能。您可以像这样定义隐式值类:
implicit class CustomAny(val self: Any) extends AnyVal {
def as[T] = self.asInstanceOf[T]
}
用法:
scala> class Figure {
| val xyz = "xyz"
| }
defined class Figure
scala> val fig = new Figure()
fig: Figure = Figure@73dce0e6
scala> Main.figname = fig
Main.figname: Any = Figure@73dce0e6
scala> Main.figname.as[Figure].xyz
res8: String = xyz
隐式值类与普通类一样昂贵。它将在编译时进行优化,它将等同于对静态对象的方法调用,而不是对新实例化对象的方法调用。
您可以在隐式值类here上找到更多信息。