-
Notifications
You must be signed in to change notification settings - Fork 0
CH10. 애노테이션과 리플렉션 (이희찬)
leeheefull edited this page Mar 20, 2023
·
1 revision
- 어떤 함수를 호출하려면 그 함수가 정의된 클래스의 이름과 함수 이름, 파라미터 이름 등을 알아야만 했다.
- 애노테이션과 리플렉션을 사용하면 이런 제약을 벗어나서 미리 알지 못하는 임의의 클래스를 다룰 수 있다.
- 애노테이션을 사용하면 라이브럴리가 요구하는 의미를 클래스에게 부여할 수 있다.
- 리플렉션을 사용하면 실행 시점에 컴파일러 내부 구조를 분석할 수 있다.
- 애노테이션에 메타데이터를 선언하면 애노테이션을 처리하는 도구가 컴파일 시점이나 실행 시점에 적절한 처리를 한다.
-
애노테이션을 적요하려는 대상 앞에 붙이면 된다.
-
“@ + 이름”으로 구성된다.
import org.junit.* class MyTest { @Test fun testTrue() { Assert.assertTrue(true) } }
-
애노테이션에 인자를 지정하는 것은 자바와 약간 다르다.
- 클래스를 인자로 지정할 떄는 “::class”를 클래스 이름 뒤에 넣어야 한다.
- 인자로 들어가는 애노테이션의 이름 앞에 “@”를 넣지 않아야 한다.
- 배열을 인자로 지정하려면 “arrayOf()” 함수를 사용한다.
-
코틀린에서 한 선언의 결과가 자바 선언에 대응하는 경우가 있다.
-
이러한 경우 사용 지점 대상 선언으로 애노테이션을 붙일 요소를 정할 수 있다.
@get: Rule val folder = TemporaryFolder() @Test fun testUsingTempFolder() { val createdFile = folder.newFile("myfile.txt") val createdFolder = folder.newFolder("subfolder") }
-
사용 지점 대상을 지정할 때 지원하는 대상 목록
- property: 프로퍼티 전체 자바에서 선언된 애노테이션에는 이 시용 지점 대상 을 사용할수없다
- field: 프로퍼티에 의해 생성되는 (뒷받침하는) 필드
- get: 프로퍼티 게터
- set: 프로퍼티 세터
- receiver: 확장 함수나 프로퍼티의 수신 객체 파라미터
- param: 생성자 파라미터
- setparam: 세터 파라미터
- delegate: 위임 프로퍼티의 위임 인스턴스를 담아둔 필드
- file: 파일 안에 선언된 최상위 함수와 프로퍼티를 담아두는 클래스
-
애노테이션 클래스는 오직 선언이나 식과 관련 있는 메타데이터의 구조를 정의하기 때문에 내부에 아무 코드도 들어있을 수 없다.
annotation class JsonExclude
-
파라미터가 있는 애노테이션을 정의하려면 애노테이션 클래스의 주 생성자에 파라미터를 선언해야 한다.
annotation class JsonName(val name: String)
-
클래스에 적용할 수 있는 애노테이션을 메타애노테이션이라고 부른다.
@Target(AnnotationTarget.ANNOTATION_CLASS) annotation class BindingAnnotation
@BindingAnnotation annotation class MyBinding
annotation class DeserializeInterface(val targetClass: KClass<out Any>)
interface Company {
val name: String
}
data class CompanyImpl (override val name: String) : Company
data class Person(
val name: String,
@DeserializeInterface(CompanyImpl::class) val company: Company
)
annotation class CustomSerializer(
val serializerClass: KClass<out ValueSerializer<*>>
)
interface ValueSerializer<T> {
fun toJsonValue(value: T): Any?
fun fromJsonValue(jsonValue: Any?): T
}
class DataSerializer : ValueSerializer<Date> {
}
data class Person(
val name: String,
@CustomSerializer(DataSerializer::class) val birthDate: Date
)
- 클래스를 인자로 받아야 하면 애노테이션 파라미터 타입에
KClass<out 허용할 클래스 이름>
- 제네릭 클래스를 인자로 받아야 하면
KClass<out 허용할 클래스 이름<*>>
- 리플렉션은 실행 시점에 동적으로 객체의 프로퍼티와 메소드에 접근할 수 있게 해주는 방법이다.
-
KClass를 사용하면 클래스 안에 있는 모든 선언을 열거하고 각 선언에 접근하거나 클래스의 상위 클래스를 얻는 등의 작업이 가능하다.
사용하는 법
class Person(val name: String, val age: Int)
import kotlin.reflect.full.* val person = Person("Alice", 29) val kClass = person.javaClass.kotlin println(kClass.simpleName) >>> Person kClass.memberProperties.forEach { println(it.name) } >>> age >>> name
정의
interface KClass<T : Any> { val simpleName: String? val qualifiedName : String? val members : Collection<KCallable<*>> val constructors : Collection<KFunction<T>> val nestedClasses : Collection<KClass<*>> ... }
-
KCallable은 함수와 프로퍼티를 아우르는 공통 상위 인터페이스다.
interface KCallable<out R> { fun call(vararg args: Any?): R ... } fun foo(x: Int) = println(x) val kFunction = ::foo kFunction.call(42) >> 42
-
KFunction<Int, Unit>에는 파라미터와 반환 값 타입 정보가 들어있다.
var counter = 0 val kProperty = ::counter kProperty.setter.call(21) println(kProperty.get()) >>> 21
-
KProperty를 통해서 프로퍼티 값을 얻을 수 있다.
-
-
코틀린 리플렉션 API 인터페이스 계층 구조