들어가기전에
리플렉션? PHP로 개발을 해오다가 자바의 리플렉션이라는 개념을 접하니까 뭐쥬? 하고 강의도 보고 검색도 해서 찾아봤다.
깃허브에 정리해뒀다! : 링크
간단히 정리
- 대충 느낌은 정확한 자바가 가지는 정적인 특징(컴파일시점에 읽어드림)을 보완하여 동적으로 런타임 시점에 처리가능하도록 해주는 것. 클래스 이름만 알고 있다면 해당 클래스에 정의된 요소들을 사용할 수 있고 객체만 아는 상황이라면 해당 객체의 클래스에 접근할 수 있는 것.
- 애노테이션(
@
)을 지정했을 때 이를 사용 가능하도록 하는 것이 리플렉션이다!
애노테이션과 리플렉션
- 함수&클래스명을 소스코드에서 정확하게 알고 있어야만 사용가능하다. <=> (애노테이션&리플렉션) 임의의 클래스를 다룰 수 있다.
애노테이션
- 문법은 자바와 거의 동일, but 자바보다 더 넓은 대상에 적용 가능(ex|파일, 식)
애노테이션 적용
- 적용하려는 대상에
@
(애노테이션) 붙이기
@Test fun testTrue()
: jUnit 프레임워크에게 이 메서드의 테스트 호출 지시
- 인자로는 원시 타입의 값, 문자열, enum, 클래스 참조, 다른 애노테이션 클래스, 그리고 지금까지 말한 요소들로 이뤄질 배열이 포함될 수 있음
지정 문법은 자바와 약간 다름
@Deprecated("Use removeAt (index) instead.", ReplaceWith("removeAt(index)"))
fun remove(index: Int) { ... }
- 클래스 애노테이션 인자로 지정 :
MyAnnotation(MyClass::class)
- ::class
붙이기
- 다른 애노테이션 인자로 지정시 : 인자로 들어가는 애노테이션 이름 앞에
@
넣지 않을 것 (위 예제 ReplaceWith참고)
- 배열 인자 지정 :
@RequestMapping(path = arrayOf("/foo", "/bar"))
: arrayOf
사용
- 가변 길이라면 :
@JavaAnnotationWithArrayValue("abc", "foo", "bar")
처럼 사용하면 arrayOf
사용 안해도 됨
- 프로퍼티를 애노테이션 인자로 사용하려면 컴파일 시점에 알아야 하기 때문에
const
변경자 붙이기
const val TEST_TIMEOUT = 100L //애노테이션 인자로 사용할 프로퍼티에 const 붙이기
@Test(timeout = TEST_TIMEOUT) fun testMethod() { ... }
애노테이션 대상
@get:Rule
//@get : 사용지점대상
//Rule : 애노테이션 이름
- 사용 지점 대상 선언으로 애노테이션 붙일 요소를 정할 수 있음
- 프로퍼티가 아니라 게터에 애노테이션이 붙는다.
- 코틀린의 필드는 기본적으로 비공개
- 지원 대상 목록 : property, field, get, set, receiver, param, setParam, delegate, file
- 컴파일러 경고를 무시하려면
@Suppress
사용하기
대표적인 예제는 직렬화, 역직렬화
data class Person (
@JsonName("alias") val firstName: String, //firstName 대신 alias로 키명 대체
@JsonExclude val age:Int? = null //제외할 필드
)
Person("Alice")
-> {"alias":"Alice"}
(직렬화->역직렬화)
- 제외 프로퍼티는 반드시 디폴트 값 설정(안하면 역직렬화시 객체 생성 불가능)
애노테이션 선언
annotation class JsonExclude
annotation class JsonName(val name: String)
- 애노테이션 클래스 정의시엔 본문이 없고 주 생성자의 모든 파라미터를 val 프로퍼티로 표시한 코틀린 클래스를 사용
메타애노테이션
- 애노테이션 클래스에 적용할 수 있는 애노테이션
- 대상, 애노테이션 유지 방식 등 여러 애노테이션 특성 지정 가능
- 컴파일러가 애노테이션을 처리하는 방법 제어
- 흔히 쓰는 것은
@Target
: 적용 가능 대상 지정
애노테이션 파라미터로 클래스 사용
KClass<out Any>
<- KClass<CompanyImpl>
- 위 코드에서는
CompanyImpl
가 파라미터임
- CompanyImpl은 Any의 하위 타입(공변성)
- out 변경자 명시 안하면 Any만 가능
애노테이션 파라미터로 제네릭 클래스 받기
KClass<out ValueSerializer<*>>
- out 명시 : ValueSerializer::class 뿐 아니라 ValueSerializer 를 구현하는 모든 클래스를 받아들인다.
<*>
: ValueSerializer를 사용해 어떤 타입의 값이든 직렬화 가능
리플렉션
- 실행 시점에 코틀린 객체 내부 관찰
- 실행 시점에만 메서드나 프로퍼티 이름을 알 수 있는 경우에 사용 가능
- 직렬화에 경우 실행 시점 전까지는 라이브러리가 직렬화할 프로퍼티나 클래스에 대한 정보를 알 수 X
코틀린 리플렉션 API
- 리플렉션 API 를 통해 실행 시점에 객체의 메서드와 프로퍼티를 열거하고 접근 할 수 있다.
- 리플렉션 API 포함 인터페이스 : KClass, KCallable, KFunction, KProperty
val person = new Person("Alice", 29)
val kClass = person.javaClass.kotlin
- 클래스를 컴파일 시점에 알고 있다면
ClassName::class
사용, 실행 시점에 obj로부터 KClass 인스턴스를 얻기 위해서는obj.javaClass.kotlin
사용
interface KCallable<out R> {
fun call(vararg args: Any?) : R
...
}
fun foo(x: Int) = println(x)
>>> val kFunction = ::foo //멤버참조
>>> kFunction.call(42) //42
- KFunction 과 KProperty 인터페이스는 모두 KCallable(
call
메서드 제공) 확장
call
메서드에 넘긴 인자 개수와 원래 함수에 정의된 파라미터 개수가 맞아 떨어져야 함
- KCallable.callBy 메서드 사용시 디폴트 파라미터 지정 가능
- KFunction0, KFunction1 등의 인터페이스는 모두 파라미터 수가 다른 함수 표현, invoke 메서드 사용 가능
- KProperty0은 최상위 프로퍼티, KProperty1은 수신 객체가 있는 프로퍼티 접근시 사용하는 인터페이스
- get 메서드로 프로퍼티 값 획득 가능
- 수신 객체라 하믄.. set이 된다는 소리
- 0은 get만 가능하고 1은 get&set 모두 가능하다는 소리임!
- KMutableProperty0, KMutableProperty1 은 KProperty0, KProperty1 확장, set으로 값 변경 가능
- KProperty1은 한번 set하고서 값 변경 안되지만 KMutableProperty0, KMutableProperty1은 계속 변경 가능하다는 소리임