2012-11-06 57 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'好得多嗎(並且顯式限定爲「醜陋」)怎麼樣? –

相關問題