2012-11-06 37 views
2

Dynamic从斯卡拉添加变量2.10.0-RC1是这样的:避免显式转换为Scala的动态类型

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,所以它的AnyFigure,但没有Figures能力。现在我可以投它,Main.figname.asInstanceOf[Figure].number,它的工作原理!这很丑陋!并且我无法向我的域用户展示此内容(我想构建内部DSL)。

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

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

回答

2

这很合乎逻辑。您明确返回Any的实例。一种解决方法是将有动态的情况下,一直以来:

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 = [email protected] 
scala> Main.figname = fig 
Main.figname: DynamicBase = [email protected] 
scala> Main.figname.bla 
res40: DynamicBase = BLA 

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

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

作为一个说明,我不得不将'selectDynamic'定义为'def selectDynamic [T](name:String):DynamicBase'而不是'def selectDynamic(name:String):DynamicBase'。多余的类型参数在这里使编译器感到高兴,否则它会在调用'as'时产生扼制(即“selectDynamic”不会接受类型参数,即使对'as'的调用是直接的并且不会通过'selectDynamic'。它看起来是一个bug。 –

+0

这个效果很好,谢谢你! – Themerius

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 = [email protected] 

scala> Main.figname = fig 
Main.figname: Any = [email protected] 

scala> Main.figname.as[Figure].xyz 
res8: String = xyz 

隐含的价值类并不昂贵喜欢像普通类。它将在编译时进行优化,它将等同于对静态对象的方法调用,而不是对新实例化对象的方法调用。

您可以在隐式值类here上找到更多信息。

+0

比直接使用'asInstanceOf'好得多吗(并且显式限定为“丑陋”)怎么样? –

相关问题