避免在scala'上进行显式强制转换;s动态类型

避免在scala'上进行显式强制转换;s动态类型,scala,Scala,我使用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 upda

我使用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
  implicit class CustomAny(val self: Any) extends AnyVal {
    def as[T] = self.asInstanceOf[T]
  }
现在,如果我想访问
Main.figname.number
它就不能工作,因为编译器认为它属于
Any
类型

但是它也是
Main.figname.isInstanceOf[Figure]==true
,所以它是
Any
Figure
,但没有
Figures
能力。现在我可以像,
Main.figname.asInstanceOf[Figure].number那样对它进行强制转换,它就可以工作了!这太难看了!我不能向我的域用户展示这一点(我想构建一个内部DSL)

注意:如果我使用而不是
Any
Figure
的超类型,它也不起作用


这是scala 2.10中的一个bug,还是一个特性?

这是非常合乎逻辑的。您将显式返回
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
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
所有实例都包装在一个动态实例中。 我们可以使用执行动态强制转换的
as
方法恢复实际类型

scala> val myString: String = Main.figname.bla.as[String]
myString: String = BLA

您可以向
任何
或任何预定义的
值类
添加任何扩展或自定义功能。您可以这样定义隐式值类:

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
  implicit class CustomAny(val self: Any) extends AnyVal {
    def as[T] = self.asInstanceOf[T]
  }
用法:

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
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
隐式值类不像常规类那样昂贵。它将在编译时得到优化,它将相当于对静态对象的方法调用,而不是对新实例化的对象的方法调用


您可以找到有关隐式值类的更多信息。

作为旁注,我必须将
selectDynamic
定义为
defselectdynamic[T](名称:String):DynamicBase
,而不仅仅是
defselectdynamic(名称:String):DynamicBase
。这里多余的类型参数是为了让编译器满意,否则它会在调用
as
(说
selectDynamic
不接受类型参数,即使对
as
的调用是直接的,并且没有经过
selectDynamic
。这似乎是一个bug。这比像OP那样直接使用
asInstanceOf
更好(并且明确限定为“丑陋”)?