2

Dynamic我从 scala 2.10.0-RC1添加变量,如下所示:

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它不起作用,因为编译器认为它是 type Any

但它也是Main.figname.isInstanceOf[Figure] == true,所以它是AnyFigure,但没有Figures能力。现在我可以像投掷它一样,Main.figname.asInstanceOf[Figure].number它的工作原理!这太丑了!而且我无法将其呈现给我的域用户(我想构建一个内部 DSL。)

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

这是 scala 2.10 中的错误还是功能?

4

2 回答 2

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

所有实例都包装在一个动态实例中。as我们可以使用执行动态转换的方法来恢复实际类型。

scala> val myString: String = Main.figname.bla.as[String]
myString: String = BLA
于 2012-11-06T16:45:23.833 回答
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

隐式值类不像普通类那样昂贵。它将在编译时进行优化,相当于对静态对象的方法调用,而不是对新实例化对象的方法调用。

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

于 2015-08-05T06:10:35.680 回答