Dynamic 타입
Dynamic 타입을 사용하려면 scala.Dynamic trait를 확장해야 한다.
scala> class Example extends Dynamic
error: extension of type scala.Dynamic needs to be enabled
by making the implicit value language.dynamics visible.
This can be achieved by adding the import clause 'import scala.language.dynamics'
or by setting the compiler option -language:dynamics.
See the Scala docs for value scala.language.dynamics for a discussion
why the feature needs to be explicitly enabled.
위와 같이 Dynamic을 확장한 클래스를 정의하면 오류가 발생한다. Dynamic 타입을 사용하려면 Dynamic 기능을 활성화해야 하는데 이는 scala.language.dynamics를 임포트해야한다.(또는 컴파일 옵션에 -language:dynamics를 추가해서) 이렇게 사용하는 이유는 동적으로 멤버를 선택하는 기능이 정적 타입체크 기능을 저하시키고 동적 멤버 선택이 경우에 따라서는 리플렉션을 사용하는데 리플렉션을 사용할 수 없는 플랫폼이 있기 때문이다. Dynamic 기능을 활성화 하지 않으면 Dynamic을 기반 트레이트로 가진 클래스, 트레이트, 오프젝트를 정의할 수 없다.
scala> import scala.language.dynamics
import scala.language.dynamics
scala> class Example extends Dynamic
defined class Example
위와 같이 import scala.language.dynamics를 사용하고 나면 오류없이 Dynamic 트레이트를 확장한 클래스를 정의할 수 있다.
qual.sel과 같이 사용했을 때 타입 확인에 실패하고 리시버인 qual의 타입이 scala.Dynamic이고 sel이 applyDynamic, applyDynamicNamed, selectDynamic, updateDynamic이 아닌 경우 다음과 같이 재작성한다.
- qual.sel에 뒤에 타입 인자리스트 [TS](선택적이다)와 인자리스트 (arg1, ..., argN)이(이때 인자가 이름있는 인자가 아니고 argN이 sequence 인자가 아니어야 한다) 오는 경우 즉 qual.sel[Ts](arg1, ..., argN)을 qual.applyDynamic[Ts]("sel")(arg1, ..., argN)으로 재작성한다.
- qual.sel에 뒤에 타입 인자리스트 [TS](선택적이다)와 이름있는 인자리스트 (x1 = arg1, ..., xN = argN)이(일부는 이름있는 인자가 아니더라도) 오는 경우 즉, qual.sel[Ts](x1 = arg1, …, xN = argN)를 qual.applyDynamicNamed[Ts]("sel")(xs1 -> arg1, …, xsN -> argN)으로 재작성 한다.
- qual.sel뒤에 인자나 할당 오퍼레이터가 없는 경우 즉, qual.sel을 qual.selectDynamic[Ts]("sel")로 재작성한다.
- qual.sel이 할당의 좌변에 오는 경우 즉, qual.sel = expr를 qual.updateDynamic(“sel”)(expr)로 재작성한다.
applyDynamic
qual.sel에 뒤에 타입 인자리스트 [TS](선택적이다)와 인자리스트 (arg1, ..., argN)이(이때 인자가 이름있는 인자가 아니고 argN이 sequence 인자가 아니어야 한다) 오는 경우 즉 qual.sel[Ts](arg1, ..., argN)을 qual.applyDynamic[Ts]("sel")(arg1, ..., argN)으로 재작성한다.
//test1.scala
import scala.language.dynamics
object Example extends Dynamic {
def applyDynamic(methodName: String)(args: Any*) {
println("methodName: " + methodName)
println("args: "+ args.mkString(","))
}
}
Example.notExistMethod("outsider", "scala", 2013)
위와 같이 작성한 코드를 실행하면 다음과 같이 실행된다.
$ scala test1.scala
methodName: notExistMethod
args: outsider,scala,2013
Example 객체에 notExistMethod라는 메서드가 존재하지 않지만 오류없이 동작하는 것을 볼 수 있다. 여기서는 받은 메서드이름과 인자를 차례대로 출력했다. 이를 이용해서 동적타입의 메서드처럼 사용할 수 있다.
applyDynamicNamed
qual.sel에 뒤에 타입 인자리스트 [TS](선택적이다)와 named 인자리스트 (x1 = arg1, ..., xN = argN)이(일부는 named 인자가 아니더라도) 오는 경우 즉, qual.sel[Ts](x1 = arg1, …, xN = argN)를 qual.applyDynamicNamed[Ts]("sel")(xs1 -> arg1, …, xsN -> argN)으로 재작성 한다.
//test2.scala
import scala.language.dynamics
object Example extends Dynamic {
def applyDynamicNamed(methodName: String)(args: (String, Any)*) {
println("methodName: " + methodName)
println("args: ")
for (i <- args) yield println(" " + i._1 + " : " + i._2)
}
}
Example.notExistMethod(nickname = "Outsider", city = "Seoul")
위의 코드를 다음과 같이 실행한다.
$ scala test2.scala
methodName: notExistMethod
args:
nickname : Outsider
city : Seoul
앞의 메서드와 비슷하지만 이번에는 인자가 named 인자이다. 이러한 경우 앞에서와 동일하게 메서드 이름을 출력하고 튜플로 받은 인자를 차례대로 출력했다.
selectDynamic
qual.sel뒤에 인자나 할당 오퍼레이터가 없는 경우 즉, qual.sel을 qual.selectDynamic[Ts]("sel")로 재작성한다.
//test3.scala
import scala.language.dynamics
object Example extends Dynamic {
def selectDynamic(field: String): String = "I have a " + field + " member!"
}
println(Example.notExist)
println(Example.outsider)
println(Example.nephilim)
메서드가 아닌 존재하지 않은 멤버변수를 호출한 경우이다. 이 경우 위처럼 selectDynamic이 적용되고 다음과 같이 출력된다.
$ scala test3.scala
I have a notExist member!
I have a outsider member!
I have a nephilim member!
updateDynamic
qual.sel이 할당의 좌변에 오는 경우 즉, qual.sel = expr를 qual.updateDynamic(“sel”)(expr)로 재작성한다.
$ test4.scala
import scala.language.dynamics
import scala.collection.mutable
object Example extends Dynamic {
private var map = mutable.Map[String, Any]()
def selectDynamic(key: String) = map(key)
def updateDynamic(key: String)(value: Any) { map(key) = value }
}
Example.a = "scala"
println(Example.a)
Example.b = "hello"
println(Example.b)
이번에는 동적 멤버에 할당을 하기 우해서 updateDynamic을 사용했고 받은 값을 map에 저장한 뒤에 해당 값을 출력하기 위해서 selectDynamic을 정의했다.
$ scala test4.scala
Example.a: Any = scala
scala
Example.b: Any = hello
hello
a, b가 존재하지 않는 필드이지만 동적으로 할당하고 가져올 수 있다.
Comments