Scala 2.10의 새로운 기능 : Dynamic 타입
스칼라는 정적 타입언어이고 정적 타입이 가진 장점들이 있는 반면 동적타입이 가진 장점들도 있다. 스칼라도 타입을 유연하게 사용할 수 있는 여러 방법을 제공하고 있지는 하지만 obj.props와 같이 사용하면 obj에 props라는 멤버가 정의되어 있거나 implicit 타입변환을 할 수 있는 등 리시버(여기서는 obj)가 해당 멤버를 정적으로 알고 있을 때만 사용할 수 있다. 이부분을 해결하기 위해서 Scala는 동적타입 언어처럼 사용할 수 있는 Dynamic 타입이 추가했는데 2.9에서는 -Xexperimental 옵션을 붙혀야 사용할 수 있었지만 2.10에서는 기본으로 사용할 수 있게 되었다.(Dynamic에 대한 글이 많지 않아서 Scala 2.10: class OhMy extends Dynamic ! 를 참고했다.)
Dynamic 타입
Dynamic 타입을 사용하려면 scala.Dynamic trait 를 확장해야 한다.
1 2 3 4 5 6 7 |
|
위와 같이 Dynamic을 확장한 클래스를 정의하면 오류가 발생한다. Dynamic 타입을 사용하려면 Dynamic 기능을 활성화해야 하는데 이는 scala.language.dynamics를 임포트해야한다.(또는 컴파일 옵션에 -language:dynamics를 추가해서) 이렇게 사용하는 이유 는 동적으로 멤버를 선택하는 기능이 정적 타입체크 기능을 저하시키고 동적 멤버 선택이 경우에 따라서는 리플렉션을 사용하는데 리플렉션을 사용할 수 없는 플랫폼이 있기 때문이다. Dynamic 기능을 활성화 하지 않으면 Dynamic을 기반 트레이트로 가진 클래스, 트레이트, 오프젝트를 정의할 수 없다.
1 2 3 4 5 |
|
위와 같이 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)으로 재작성한다.
1 2 3 4 5 6 7 8 9 10 11 |
|
위와 같이 작성한 코드를 실행하면 다음과 같이 실행된다.
1 2 3 |
|
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)으로 재작성 한다.
1 2 3 4 5 6 7 8 9 10 11 12 |
|
위의 코드를 다음과 같이 실행한다.
1 2 3 4 5 |
|
앞의 메서드와 비슷하지만 이번에는 인자가 named 인자이다. 이러한 경우 앞에서와 동일하게 메서드 이름을 출력하고 튜플로 받은 인자를 차례대로 출력했다.
selectDynamic
qual.sel뒤에 인자나 할당 오퍼레이터가 없는 경우 즉, qual.sel을 qual.selectDynamic[Ts]("sel")로 재작성한다.
1 2 3 4 5 6 7 8 9 10 |
|
메서드가 아닌 존재하지 않은 멤버변수를 호출한 경우이다. 이 경우 위처럼 selectDynamic이 적용되고 다음과 같이 출력된다.
1 2 3 4 |
|
updateDynamic
qual.sel이 할당의 좌변에 오는 경우 즉, qual.sel = expr를 qual.updateDynamic(“sel”)(expr)로 재작성한다.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
|
이번에는 동적 멤버에 할당을 하기 우해서 updateDynamic을 사용했고 받은 값을 map에 저장한 뒤에 해당 값을 출력하기 위해서 selectDynamic을 정의했다.
1 2 3 4 5 |
|
a, b가 존재하지 않는 필드이지만 동적으로 할당하고 가져올 수 있다.
Comments