초식 scala 반사

우 리 는 scala 컴 파일 러 가 scala 코드 를 JVM 바이트 코드 로 컴 파일 할 것 이라는 것 을 알 고 있 습 니 다.컴 파일 과정 에서 scala 특유 의 유형 정 보 를 지 울 것 입 니 다.scala-2.10 이전에 scala 에서 자바 의 반사 체 제 를 이용 할 수 밖 에 없 었 지만 자바 반사 체 제 를 통 해 얻 은 것 은 지 운 후의 유형 정보 일 뿐 scala 의 특정한 유형 정 보 를 포함 하지 않 습 니 다.scala-2.10 부터 scala 는 자신의 반사 체 제 를 실현 했다.우 리 는 scala 의 반사 체 제 를 통 해 scala 의 유형 정 보 를 얻 을 수 있다.scala 반 사 는 운행 시 반사 와 컴 파일 시 반 사 를 포함 합 니 다.본 고 는 주로 운행 시 반사 되 는 용법 을 논술 하고 scala 개발 자 들 이 참고 할 수 있 도록 합 니 다.구체 적 인 원리 세부 사항 은 공식 문 서 를 보십시오.본 논문 에서 언급 한 코드 예 는 scala-2.10.4 를 바탕 으로 하 는 것 입 니 다.다 르 면 scala 버 전 을 확인 하 십시오.
주어진 형식 이나 대상 인 스 턴 스 는 scala 를 통 해 실 행 될 때 반 사 됩 니 다.1)실 행 될 때 유형 정 보 를 얻 을 수 있 습 니 다.2)유형 정 보 를 통 해 새로운 대상 을 예화 한다.3)대상 에 접근 하거나 호출 하 는 방법 과 속성 등.다음은 운행 시 반사 되 는 기능 을 예 로 들 어 논술 한다.
런 타임 형식 정보 가 져 오기
scala 가 실 행 될 때 유형 정 보 는 TypeTag 대상 에 저 장 됩 니 다.컴 파일 러 는 컴 파일 과정 에서 유형 정 보 를 TypeTag 에 저장 하고 실행 기간 에 가 져 옵 니 다.우 리 는 typeTag 방법 을 통 해 TypeTag 형식 정 보 를 얻 을 수 있 습 니 다.
scala> import scala.reflect.runtime.universe._
import scala.reflect.runtime.universe._

scala> typeTag[List[Int]]
res0: reflect.runtime.universe.TypeTag[List[Int]] = TypeTag[scala.List[Int]]

scala> res0.tpe
res1: reflect.runtime.universe.Type = scala.List[Int]

상기 scala REPL 에 따 르 면 typeTag 방법 으로 List[Int]형식의 TypeTag 대상 을 얻 을 수 있 습 니 다.이 대상 은 List[Int]의 상세 한 유형 정 보 를 포함 하고 있 습 니 다.TypeTag 대상 의 tpe 방법 을 통 해 Type 대상 이 구체 적 인 유형 정 보 를 밀봉 하면 이 Type 대상 의 유형 정 보 는 유형 매개 변수 Int 까지 정확 합 니 다.유형 정보 만 얻 는 것 이 라면 더 간편 한 방법 이 있다 면 type:Of 방법 을 통 해.
scala> import scala.reflect.runtime.universe._
import scala.reflect.runtime.universe._

scala> typeOf[List[Int]]
res0: reflect.runtime.universe.Type = scala.List[Int]

이때 어떤 사람 은 type:Tag 방법 은 구체 적 인 유형 을 전달 해 야 하 는데 유형 을 미리 알 고 TypeTag 를 해 야 무슨 소 용이 있 느 냐 고 묻는다.우 리 는 임의의 대상 의 유형 정 보 를 얻 을 수 있 는 방법 을 써 도 무방 하 다.
scala> val ru = scala.reflect.runtime.universe
ru: scala.reflect.api.JavaUniverse = scala.reflect.runtime.JavaUniverse@6dae22e7

scala> def getTypeTag[T: ru.TypeTag](obj: T) = ru.typeTag[T]
getTypeTag: [T](obj: T)(implicit evidence$1: ru.TypeTag[T])ru.TypeTag[T]

scala> val list = List(1, 2, 3)
list: List[Int] = List(1, 2, 3)

scala> val theType = getTypeTag(list).tpe
theType: ru.Type = List[Int]

위의 scala REPL 에 따 르 면 방법 getTypeTag 는 임의의 대상 의 유형 정 보 를 얻 을 수 있 습 니 다.주의 방법 중의 문맥 정의T: ru.TypeTag는 T 에서 TypeTag[T]로 의 암시 적 전환 이 존재 한 다 는 것 을 나타 냅 니 다.앞에서 말 했 듯 이 TypeTag 대상 은 컴 파일 기간 에 컴 파일 러 에 의 해 생 성 되 었 습 니 다.이 문맥 정 의 를 추가 하지 않 으 면 컴 파일 러 는 T 에 TypeTag 대상 을 생 성하 지 않 습 니 다.물론 REPL 에 표 시 된implicit evidence$1: ru.TypeTag[T]처럼 암시 적 인 파 라 메 터 를 통 해 문맥 정 의 를 대체 할 수도 있다.유형 정보(Type instance)를 얻 으 면 이 Type 대상 을 통 해 더 자세 한 유형 정 보 를 조회 할 수 있 습 니 다.
scala> val decls = theType.declarations.take(10)
decls: Iterable[ru.Symbol] = List(constructor List, method companion, method isEmpty, method head, method tail, method ::, method :::, method reverse_:::, method mapConserve, method ++)

여기에서 TypeTag 대상 이 Type 대상 을 봉 인 했 음 을 알 수 있 습 니 다.Type 대상 을 통 해 방법 과 속성 등 상세 한 유형 정 보 를 얻 을 수 있 습 니 다.typeTag 방법 을 통 해 TypeTag 대상 을 얻 을 수 있 습 니 다.typeOf 방법 을 통 해 Type 대상 을 얻 을 수 있 습 니 다.컴 파 일 러 에 의 해 지 워 지지 않 은 완전한 scala 형식 정 보 를 포함 하고 이에 대응 하 는 곳 을 포함 합 니 다.지 운 후의 유형 정 보 를 얻 으 려 면 전통 적 인 방법 은 자바 의 반사 체 제 를 통 해 이 루어 질 수 있 지만 scala 도 이 기능 을 제공 합 니 다.classTag 방법 을 통 해 ClassTag 대상 을 얻 을 수 있 습 니 다.ClassTag 는 지 운 후의 유형 정 보 를 밀봉 하고 classOf 방법 을 통 해 Class 대상 을 얻 을 수 있 습 니 다.이것 은 자바 반사 중의 Class 대상 과 일치 합 니 다.
scala> import scala.reflect._
import scala.reflect._

scala> val clsTag = classTag[List[Int]]
clsTag: scala.reflect.ClassTag[List[Int]] = scala.collection.immutable.List

scala> clsTag.runtimeClass
res0: Class[_] = class scala.collection.immutable.List

scala> val cls = classOf[List[Int]]
cls: Class[List[Int]] = class scala.collection.immutable.List

scala> cls.[tab   ]
asInstanceOf              asSubclass                cast                      desiredAssertionStatus    getAnnotation
getAnnotations            getCanonicalName          getClassLoader            getClasses                getComponentType
getConstructor            getConstructors           getDeclaredAnnotations    getDeclaredClasses        getDeclaredConstructor
getDeclaredConstructors   getDeclaredField          getDeclaredFields         getDeclaredMethod         getDeclaredMethods
getDeclaringClass         getEnclosingClass         getEnclosingConstructor   getEnclosingMethod        getEnumConstants
getField                  getFields                 getGenericInterfaces      getGenericSuperclass      getInterfaces
getMethod                 getMethods                getModifiers              getName                   getPackage
getProtectionDomain       getResource               getResourceAsStream       getSigners                getSimpleName
getSuperclass             getTypeParameters         isAnnotation              isAnnotationPresent       isAnonymousClass
isArray                   isAssignableFrom          isEnum                    isInstance                isInstanceOf
isInterface               isLocalClass              isMemberClass             isPrimitive               isSynthetic
newInstance               toString

상기 scala REPL 에서 볼 수 있 듯 이 ClassTag 대상 은 Class 대상 을 포함 하고 있 으 며,Class 대상 을 통 해 지 워 진 형식의 정보 만 얻 을 수 있 으 며,scala REPL 에서 tab 로 보완 하면 Class 대상 을 통 해 얻 을 수 있 는 정 보 를 볼 수 있다.
런 타임 형식 예화
Type 대상 을 통 해 지우 지 않 은 상세 한 유형 정 보 를 얻 을 수 있다 는 것 을 알 고 있 습 니 다.다음은 Type 대상 의 정 보 를 통 해 구조 방법 을 찾 고 유형 을 예화 하 는 대상 을 찾 습 니 다.
scala> case class Person(id: Int, name: String)
defined class Person

scala> val ru = scala.reflect.runtime.universe
ru: scala.reflect.api.JavaUniverse = scala.reflect.runtime.JavaUniverse@3e9ed70d

scala> val m = ru.runtimeMirror(getClass.getClassLoader)
m: ru.Mirror = JavaMirror with scala.tools.nsc.interpreter.IMain$TranslatingClassLoader@a57fc5f ...

scala> val classPerson = ru.typeOf[Person].typeSymbol.asClass
classPerson: ru.ClassSymbol = class Person

scala> val cm = m.reflectClass(classPerson)
cm: ru.ClassMirror = class mirror for Person (bound to null)

scala> val ctor = ru.typeOf[Person].declaration(ru.nme.CONSTRUCTOR).asMethod
ctor: ru.MethodSymbol = constructor Person

scala> val ctorm = cm.reflectConstructor(ctor)
ctorm: ru.MethodMirror = constructor mirror for Person.<init>(id: scala.Int, name: String): Person (bound to null)

scala> val p = ctorm(1, "Mike")
p: Any = Person(1,Mike)

위의 scala REPL 코드 와 같이 Type 대상 을 통 해 관련 정 보 를 얻 으 려 면 반드시 Mirror 를 빌려 야 한다.Mirror 는 등급 별로 구분 되 는데 ClassLoader Mirror,ClassMirror,InstanceMirror,ModuleMirror,MethodMirror,FieldMirror 가 있다.ClassLoader Mirror 를 통 해 ClassMirror,InstanceMirror,ModuleMirror,MethodMirror,FieldMirror 를 만 들 수 있다.ClassMirror,InstanceMirror 를 통 해 MethodMirror,FieldMirror 를 만 들 수 있 습 니 다.ModuleMirror 는 하나의 대상 을 처리 하 는 데 사 용 됩 니 다.보통 object 에서 정의 합 니 다.상기 코드 에서 알 수 있 듯 이 먼저 ClassLoader Mirror 를 가 져 온 다음 에 이 Mirror 를 통 해 ClassMirror 를 만 들 고 MethodMirror 를 계속 만 들 며 이 MethodMirror 를 통 해 구조 함 수 를 호출 합 니 다.하나의 Mirror 에서 다른 Mirror 를 만 들 려 면 Symbol 을 지정 해 야 합 니 다.Symbol 은 바 인 딩 이름과 하나의 실체 입 니 다.ClassSymbol,MethodSymbol,FieldSymbol 등 이 있 습 니 다.Symbol 의 획득 은 Type 대상 방법 으로 조회 합 니 다.예 를 들 어 상기 코드 에서 declaration 방법 으로 구조 함수 의 Symbol 을 조회 합 니 다.
런 타임 클래스 구성원 접근
다음 예 를 들 어 방문 실행 시 클래스 구성원 을 논술 합 니 다.마찬가지 로 저 희 는 FieldMirror 를 점차적으로 만들어 서 클래스 구성원 을 방문 해 야 합 니 다.
scala> case class Person(id: Int, name: String)
defined class Person

scala> val ru = scala.reflect.runtime.universe
ru: scala.reflect.api.JavaUniverse = scala.reflect.runtime.JavaUniverse@3e9ed70d

scala> val m = ru.runtimeMirror(getClass.getClassLoader)
m: ru.Mirror = JavaMirror with scala.tools.nsc.interpreter.IMain$TranslatingClassLoader@a57fc5f ...

scala> val p = Person(1, "Mike")
p: Person = Person(1,Mike)

scala> val nameTermSymb = ru.typeOf[Person].declaration(ru.newTermName("name")).asTerm
nameTermSymb: ru.TermSymbol = value name

scala> val im = m.reflect(p)
im: ru.InstanceMirror = instance mirror for Person(1,Mike)

scala> val nameFieldMirror = im.reflectField(nameTermSymb)
nameFieldMirror: ru.FieldMirror = field mirror for Person.name (bound to Person(1,Mike))

scala> nameFieldMirror.get
res0: Any = Mike

scala> nameFieldMirror.set("Jim")

scala> p.name
res2: String = Jim

위 코드 와 같이 등급 별 ClassLoader Mirror->InstanceMirror->FieldMirror 를 통 해 FieldMirror 를 얻 고 Type 대상 호출 방법declaration(ru.newTermName("name"))을 통 해 name 필드 의 Symbol 을 얻 으 며 FieldMirror 의getset방법 으로 구성원 변 수 를 방문 하고 수정 합 니 다.
이렇게 많아문 제 는 컴 파일 러 가 지 운 후의 유형 정 보 를 방문 하 느 냐,지 운 전의 유형 정 보 를 방문 하 느 냐 하 는 것 이다.지 운 후의 유형 정 보 를 방문 하 느 냐,자바 와 scala 의 반 사 를 사용 하 느 냐 하 는 것 이다.그러나 지 운 전의 유형 정 보 를 방문 하려 면 scala 의 반 사 를 사용 해 야 한다.자바 의 반 사 는 지 워 지기 전의 정 보 를 모 르 기 때문이다.
밤 을 들 어 한 걸음 한 걸음 분석 하고 먼저 하나의 기본 클래스 A 를 정의 한다.이 는 추상 적 인 유형의 구성원 T 를 포함 한 다음 에 각각 두 개의 키 클래스 B 와 C 를 파생 시킨다.
scala> class A {
     | type T
     | val x: Option[T] = None
     | }
defined class A

scala> class B extends A
defined class B

scala> class C extends B
defined class C

현재 B 와 C 의 한 대상 을 각각 실례 화하 고 추상 적 인 유형 T 를 String 으로 구체화 한다.
scala> val b = new B { type T = String }
b: B{type T = String} = $anon$1@446344a8

scala> val c = new C { type T = String }
c: C{type T = String} = $anon$1@195bc0a4

현재 자바 의 반사 체 제 를 통 해 대상 b 와 c 의 운행 시 유형 이 부자 관계 인지 판단 합 니 다.
scala> b.getClass.isAssignableFrom(c.getClass)
res3: Boolean = false

자바 의 반사 판단 대상 c 의 운행 시 유형 이 대상 b 의 운행 시 유형의 하위 클래스 가 아 닌 것 을 볼 수 있 습 니 다.그러나 우리 의 정 의 를 보면 대상 c 의 유형 은 대상 b 의 유형의 하위 클래스 여야 한다.여기 서 우 리 는 컴 파일 러 를 생각 할 것 이다.실제 대상 b 와 c 를 예화 할 때 실제 적 으로 익명 류 를 통 해 예화 되 었 고 처음에 유형 정 보 를 컴 파일 할 때 지 워 져 익명 류 로 바 뀌 었 다.다음은 scala 의 반사 체 제 를 통 해 대상 b 와 c 의 운행 시 유형 이 부자 관계 인지 판단 한다.
scala> val ru = scala.reflect.runtime.universe
ru: scala.reflect.api.JavaUniverse = scala.reflect.runtime.JavaUniverse@3e9ed70d

scala> def isSubClass[T: ru.TypeTag, S: ru.TypeTag](x: T, y: S): Boolean = {
     | val leftTag = ru.typeTag[T]
     | val rightTag = ru.typeTag[S]
     | leftTag.tpe <:< rightTag.tpe
     | }
isSubClass: [T, S](x: T, y: S)(implicit evidence$1: ru.TypeTag[T], implicit evidence$2: ru.TypeTag[S])Boolean

scala> isSubClass(c, b)
res5: Boolean = true

상기 코드 에서 볼 수 있 듯 이 scala 의 반 사 를 통 해 얻 은 유형 정 보 는 우리 가 처음에 정의 한 것 에 부합된다.따라서 scala 에 서 는 자바 의 반 사 를 사용 하지 않 고 scala 의 반 사 를 사용 하 는 것 이 좋 습 니 다.컴 파일 후 자바 의 반 사 를 통 해 얻 은 결 과 는 생각 보다 그렇지 않 을 수 있 기 때 문 입 니 다.

좋은 웹페이지 즐겨찾기