피할

2012-11-06 6 views
2
내가 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입니다. 이 AnyFigure하지만 Figures 능력을 가지고하지 않도록

는하지만, 또한 Main.figname.isInstanceOf[Figure] == true입니다. 이제 나는 그것을 캐스팅 할 수 있습니다. Main.figname.asInstanceOf[Figure].number 그리고 작동합니다! 이것은 추한 것입니다! 그리고 내 도메인 사용자에게 이것을 제시 할 수는 없습니다. (내부 DSL을 만들고 싶습니다.)

참고 : Any 대신에 Figure의 상위 유형을 사용하면 작동하지 않습니다.

이 문제는 scala 2.10의 버그입니까?

답변

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

부연 설명으로'def selectDynamic (name : String) : DynamicBase' 대신'selectDynamic'을'def selectDynamic [T] (name : String) : DynamicBase'로 정의해야했습니다. 불필요한 형 매개 변수는 컴파일러를 행복하게 만들 때 사용합니다. 그렇지 않으면'as'에 대한 호출이 직접적이고 통과하지 않더라도'selectDynamic' doe는 형식 매개 변수를 취하지 않는다고 말합니다 –

+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

OP가 (그리고 "못생긴"것으로 명시 적으로 규정 한 것처럼) asInstanceOf를 직접 사용하는 것보다 더 좋은 방법은 무엇입니까? –

관련 문제