Kotlin-In-Action | #10. 애노테이션과 리플렉션

보람·2022년 5월 28일
1

Kotlin-In-Action

목록 보기
11/12

들어가기전에

리플렉션? 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은 계속 변경 가능하다는 소리임
profile
백엔드 개발자

0개의 댓글