[Kotlin in Action] 10장 애노테이션과 리플렉션

Sdoubleu·2023년 4월 25일
0

Kotlin in Action

목록 보기
9/9
post-thumbnail

10장에서 다루는 내용

  1. 애노테이션 적용과 정의
  2. 리플렉션을 사용해 실행 시점에 객체 내부 관찰
  3. 코틀린 실전 프로젝트 예제

어떤 함수를 호출하려면 그 함수가 정의된 클래스의 이름과 함수 이름, 파라미터 이름 등을 알아야 한다
-> 그런 제약을 벗어나게 해주는 것이 애노테이션리플렉션

  • 애노태이션을 사용하면
    라이브러리가 요구하는 의미를 클래스에게 부여할 수 있다

  • 리플렉션을 사용하면
    실행 시점에 컴파일러 내부 구조를 분석할 수 있다


10.1 애노테이션 선언과 적용

  • 메타데이터를 선언에 추가하면 애노테이션을 처리하는 도구가 컴파일 시점이나 실행 시점에 적절한 처리를 해준다

10.1.1 애노테이션 적용

  • 애노테이션을 적용하려면 적용하려는 대상 앞에 애노테이션을 붙이면 된다
    -> 함수/클래스 등 여러 다른 코드 구성 요소에 애노테이션을 붙일 수 있다

  • @Deprecated 애노테이션
    -> 특정 함수, 클래스, 필드, 생성자 등에 달아 더이상 사용하지 말라는 경고를 주기 위한 용도이다

@Deprecated("Use removeAt(index) instead.", ReplaceWith("removeAt(index)"))
fun remove(index: Int) { ... }

fun main() {
	remove(2)
}

@Deprecated("Use removeAt(index) instead.", ReplaceWith("removeAt(index)")
, level = DeprecationLevel.ERROR)
fun remove(index: Int) { ... }

fun main() {
	remove(2)
}

애노테이션 인자로는 원시 타입의 값, 문자열, enum, 클래스 참조, 다른 애노테이션 클래스, 지금까지 말한 요소들로 이뤄진 배열이 들어갈 수 있다

  • 코틀린의 애노테이션 인자를 지정하는 문법
  1. 클래스를 애노테이션 인자로 지정할 때는 @MyAnnotation(MyClass::class) 처럼 ::class를 클래스 이름 뒤에 넣어야 한다
  2. 다른 애노테이션을 인자로 지정할 때는 인자로 들어가는 애노테이션의 이름 앞에 @를 넣지 않아야 한다
  3. 배열을 인자로 지정하려면 @RequestMapping(path = arrayOf("/foo","/bar"))처럼
    arrayOf 함수를 사용한다
    자바에서 선언한 애노테이션 클래스를 사용한다면 value 라는 이름의 파라미터가
    필요에 따라 자동으로 가변 길이 인자로 변환된다
    JavaAnnotationWithArrayValue("abc","foo")처럼 arrayOf 함수를 안써도 된다
  • 프로퍼티 애노테이션 인자로 사용하려면 그 앞에 const 변경자를 붙여야 한다
    -> 컴파일러는 const가 붙은 프로퍼티를 컴파일 시점 상수로 취급한다

10.1.2 애노테이션 대상

  • 코틀린 선언과 대응하는 여러 자바 선언에 각각 애노테이션을 붙여야할 때가 있다
    ex)
    코틀린 프로퍼티 <-> 자바 필드와 게터 메소드 선언과 대응
    변경 가능한 프로퍼티 <-> 자파 필드와 게터 메소드 선언 + 세터 메소드/파라미터

-> 애노테이션을 붙일 때 이런 요소 중 어떤 요소에 애노테이션을 붙일지 표시할 필요가 있다

  • 사용 지점 대상 선언으로
    애노테이션을 붙일 요소를 정할 수 있다

get은 @Rule 애노테이션을 프로퍼티 게터에 적용하라는 뜻이다

  • 제이유닛(JUnit)에서는 각 테스트 메소드 앞에 그 메소드를 실행하기 위한 규칙을 지정할 수 있다
    -> 규칙을 지정하려면 공개 필드 / 메소드 앞에 @Rule을 붙여야 한다

  • @Rule은 필드에 적용되지만
    🔥코틀린의 필드는 기본적으로 비공개이기 때문에 예외 발생할 수도 있다

  • 코틀린으로 애노테이션을 선언하면 직접 사용할 수 있는 애노테이션을 만들 수 있다

  1. property
    프로퍼티 전체. 자바에서 선언된 애노테이션에는 이 사용 지점 대상을 사용할 수 없다
  2. field
    프로퍼티에 의해 생성되는 (뒷받침하는) 필드
  3. get
    프로퍼티 게터
  4. set
    프로퍼티 세터
  5. reciver
    확장 함수나 프로퍼티의 수신 객체 파라미터
  6. param
    생성자 파라미터
  7. setparam
    세터 파라미터
  8. delegate
    위임 프로퍼티의 위임 인스턴스를 담아둔 필드
  9. file
    파일 안에 선언된 최상위 함수와 프로퍼티를 담아두는 클래스
  • 가장 많이 쓰이는 애노테이션은 @Suppress
    -> 컴파일러 경고를 무시하기 위해서 사용

10.1.3 애노테이션을 활용한 JSON 직렬화 제어

애노테이션을 사용하는 고전적인 예제
-> 객체 직렬화 제어

  • 직렬화
    객체를 저장장치에 저장하거나 네트워크를 통해 전송하기 위해 텍스트나 이진 형식으로 변환하는 것
    -> 직렬화에 자주 쓰이는 형식: JSON
  • 역직렬화
    텍스트는 이진 형식으로 저장된 데이터로부터 원래의 객체를 만들어낸다
예제) Person 클래스를 직렬화하고 역직렬화

- 직렬화
data class Person(val name: String, val age: Int)
>>> val person = Person("Kim", 29)
>>> println(serialize(person))
{"age": 29, "name": "Kim"}

-> 객체 인스턴스의 JSON 표현은 키/값 쌍으로 이뤄진 객체를 표현한다
키/값 쌍은 각 인스턴스의 프로퍼티 이름과 값을 표현한다

- 역직렬화
>>> val json = """{"name": "Kim", "age": 29}"""
>>> println(deserialize<Person>(json))
Person(name=Kim,age=29)

-> JSON에는 객체의 타입이 저장되지 않기 때문에
JSON 데이터로부터 인스턴스를 만들려면 타입 인자로 클래스를 명시해야 한다
  • 애노테이션을 활용해 객체를 직렬화/역직렬화하는 방법을 제어할 수 있다
  • 객체를 JSON으로 직렬화할 때 제이키드 라이브러리는
    기본적으로 모든 프로퍼티를 직렬화하며 프로퍼티 이름을 키로 사용한다
    -> 애노테이션을 사용하면 이런 동작을 변경할 수 있다
  • @JsonExclude 애노테이션
    직렬화나 역직렬화 시 그 프로퍼티를 무시할 수 있다
    -> 프로퍼티에는 반드시 디폴트 값을 지정해야 한다
  • @JsonName 애노테이션
    프로퍼티를 표현하는 키/값 쌍의 키로 프로퍼티 이름 대신 애노테이션이 지정한 이름을 쓰게할 수 있다
data class Person(
	@JsonName("alias") val firstName: String,
    @JsonExclude val age: Int? = null
)

10.1.4 애노테이션 선언

  • 애노테이션 클래스는
    오직 선언이나 식과 관련 있는 메타데이터의 구조를 정의하기 때문에
    내부에 아무런 코드도 들어올 수 없다
    -> 컴파일러는 애노테이션 클래스에서 본문을 정의하지 못하게 막는다

10.1.5 메타애노테이션: 애노테이션을 처리하는 방법 제어

애노테이션 클래스에도 애노테이션을 붙일 수 있다

  • 메타애노테이션
    애노테이션 클래스에 적용할 수 있는 애노테이션

가장 많이 쓰이는 매타애노테이션은 @Target
@Targer 메타애노테이션은 애노테이션을 적용할 수 있는 요소의 유형을 지정한다

애노테이션 클래스에 대해 구체적인 @Target을 지정하지 않으면 모든 선언에 적용할 수 있는 애노테이션이 된다
-> 💣하지만, 제이키드 라이브러리는 프로퍼티 애노테이션만을 사용하므로
애노테이션 클래스에 꼭 @Target을 지정해야 한다

애노테이션이 붙을 수 있는 대상이 정의된 이넘(enum)은 AnnotationTarget이다
-> 둘 이상의 대상을 한 꺼번에 처리할 수도 있다
ex) @Target(AnnotationTarget.CLASS, AnnotationTarget.METHOD)

10.1.6 애노테이션 파라미터로 클래스 사용

클래스 참조를 파라미터로 하는 애노테이션 클래스를 선언하면
-> 어떤 클래스를 선언 메타데이터로 참조할 수 있다

  • 제이키드 라이브러이에 있는 @DeserializeInterface
    인터페이스 타입인 프로퍼티에 대한 역직렬화를 제어할 때 쓰는 애노테이션
@DeserialzeInterface 사용법

interface Company {
	val name: String
}

data class CompanyImpl(override val name: String): Company

data class Person(
	val name: String,
    @DeserialzeInterface(CompanyImpl::class) val company: Company
)

10.1.7 애노테이션 파라미터로 제네릭 클래스 받기


10.2 리플렉션: 실행 시점에 코틀린 객체 내부 관찰

10.2.1 코틀린 리플렉션 API: KClass, KCallable, KFunction, KProperty

10.2.2 리플렉션을 사용한 객체 직렬화 구현

10.2.3 애노테이션을 활용한 직렬화 제어

10.2.4 JSON 파싱과 객체 역직렬화

10.2.5 최종 역직렬화 단계: callBy(), 리플렉션을 사용해 객체 만들기

profile
개발자희망자

0개의 댓글