애노테이션과 리플렉션

애노테이션을 통해서는 라이브러리가 요구하는 의미를 클래스에서 부여하는 것이 가능하고시점에 컴파일러 내부 구조를 분석하는 것이 가능하다

애노테이션 선언과 적용

애노테이션의 사용법은 자바에서 사용하는 것 처럼 그냥 애노테이션 선언 시, 메소드, 필드 등 적용범위에 대해서 적용하는데 그렇게 적용한 곳에서 사용할 수 있다 근데 지정하는데 있어서 살짝은 다르다고 한다

  • 클래스를 애노테이션 인자로 지정할 때는 @MyAnnotation(MyClass::class) 이렇게 ::class 를 클래스 이름 뒤에 넣어주어야 한다

  • 다른 애노테이션을 인자로 지정할 때는 인자로 들어가는 애노테이션의 이름 앞에 @을 넣지 않아야 한다

  • 배열을 인자로 지정하기 위해서라면 arrayOf 함수를 사용한다

애노테이션 인자를 컴파일 시점에 알아야 한다 > 그래서 임의의 프로퍼티를 인자로 지정할 수는 없다 만약에 프로퍼티를 애노테이션 인자로 사용하기 위해서는 const 키워드를 사용해야 한다 컴파일러는 const가 부은 프로퍼티를 컴파일 시점 상수로 취급한다

json 직렬화는 객체 > 텍스트나 이진형태로 변환하는 방식, 역직렬화는 반대로 변환하는 방식이다 여기서 @JsonExclude 애노테이션을 통해서 직렬화, 역직렬화 시 해당 프로퍼티를 무시할 수 있다 또한 @JsonName 애노테이션을 사용하면 프로퍼티를 표현하는 키-값 쌍을 키로 프로퍼티 이름 대신 애노테이션이 지정한 이름으로 쓰게 할 수 있다

애노테이션을 선언하는 방법은 이러하다 간단하게 annotation 으로해서 생성하면 annotation class 뭐시기 이렇게 생성된다 이렇게 보면 일반 클래스와 차이는 없지만 단순하게 그냥 앞에 annotation 키워드가 붙냐 안붙냐 차이이다 하지만 애노테이션이라는 클래스는 오직 선언이나 식과 관련 있는 메타데이터의 구조를 정의하기 때문에 아무거나 들어갈 순 없다 자바와의 차이를 생각해보면 코틀린에서는 annotation class JsonName(val name: String) 그리고 애노테이션 클래스에서는 모든 파라미터 앞에 항상 val 키워드를 추가해야 한다 자바에서는 public @interface JsonName{ String value(); } 이런식으로 작성한다

메타애노테이션이란 애노테이션 클래스의 내부에 붙혀준 애노테이션을 보고 메타애노테이션이라고 한다 자주 사용하는 것들은 @Target이고 이 애노테이션을 통해서 해당 애노테이션을 적용할 수 있는 요소의 유형을 지정한다 꼭 있어야 애노테이션을 이제 붙히는 것이 가능하니 기억하자 그리고 해당 리스트는 AnnotationTarget 이라는 enum 값에 들어 있으니까 사용하는 것이 가져와서 리스트 선언하면 된다 그리고 @Retention 이라는 애노테이션은 정의 중인 애노테이션 클래스를 소스에서만 유지할 것인지, .class파일에 넣을 것인지, 실행시점에만 접근가능하게 할 것인지에 대한 메타 애노테이션이다

리플렉션

리플렉션의 개념은 실행 시점에 객체의 프로퍼티와 메소드에 접근할 수 있게 해주는 방법이다 > 컴파일러가 실제 메소드나 프로퍼티의 이름을 적어두면 이놈들을 컴파일 시점에 정적으로 찾아서 해당하는 선언이 실제로 존재하는 것을 보장해준다 근데 타입과 관계없이 객체를 다뤄야 하거나 객체가 제공하는 메소드나 프로퍼티 이름을 실행 시점에만 알 수 있는 경우에는 리플렉션을 사용해야 한다 예시로는 JSON 직렬화이다

일단 리플렉션을 사용하기 위해서는 2가지의 리플렉션 API를 사용해야 한다 java.lang.reflect 패키지를 통해서 제공하는 표준 리플렉션 > 코틀린은 자바 바이트코드로 완벽하게 호환되기 때문에 자바 패키지를 사용할 수 있다 kotlin.reflect 패키지를 통해 제공하는 코틀린 리플렉션 api > 여기에서는 자바에서는 없는 프로퍼티나 널이 될 수 있는 타입과 같은 코틀린 고유 개념에 대한 리플렉션 제공한다

코틀린 리플렉션으로는 KClass, KCallable, KFunction, KProperty 이렇게 존재한다 코틀린 리플렉션으로 처음 접하게 되는 건 클래스를 표현는 KClass 이다 > 이놈은 java.lang.Class에 해당하고 이걸 사용하면 클래스 안에 있는 모든 선언을 열거하고 각 선언에 접근하거나 클래스의 상위 클래스를 얻는 등의 작업이 가능하다 그리고 사용항법은로는 MyClass::class 이렇게 선언함으로써 KClass의 인스턴스를 얻는 것이 가능하다 실행 시점에 객체의 클래스를 얻으려면 먼저 객체의 javaClass 프로퍼티를 통해서 객체의 자바 클래스를 얻어야 한다

그리고 이것의 상위 개념으로는 KCallable 이라는 것이 있다 > 클래스의 모든 멤버의 목록이 KCallable 인스턴스의 컬렉션이다 그리고 정확하게 KCallable은 함수와 프로퍼티를 아우르는 공통 상위 인터페이스이고 그 안에 call 메소드를 통해서 함수나 프로퍼티의 getter를 호출한다 call을 사용할때는 함수 인자를 vararg 리스트로 전달함을 통해서 call 이라는 메소드를 통해서 함수를 호출하는 것이 가능하다

::foo 식의 값 타입이 리플렉션 API에 있는 KFunction 클래스의 인스턴스이다 > 이 함수 참조가 가리키는 함수를 호출하기 위해서는 KCallable.call 메소드를 호출한다 근데 KFunction 을 통해서 invoke 메소드로 함수를 호출하는 것도 가능은 하다 하지만 KFunction의 invoke 메소드를 초훌할 때는 인자 개수나 타입이 맞아 떨어지지 않으면 컴파일이 안된다 결론으로는 KFunction의 인자 타입과 반환 타입을 모두 다 안다면 invoke 메소드를 호출하는 것이 더 좋다 대신 call 메소드는 모든 타입의 함수에 적용할 수 있다는 일반적인 메소드이지만 타입 안전성을 보장하지 않는다

마지막 KProperty의 call 메소드도 있다. 요 KProperty의 call은 프로퍼티의 getter를 호출하긴하는데 이것 보다 get 메소드가 더 좋다 이 get 메소드에 접근하려면 프로퍼티가 선언된 방법에 따라 올바른 인터페이스를 사용해야 한다

예시로 들어준 직렬화, 역직렬화의 과정에서 여러가지 애노테이션이 있다 @JsonExclude, @JsonName, @CustomSerializer 이렇게 있다 @JsonExclude은 프로퍼티를 직렬화하는 과정에서 특정 프로퍼티를 제외하고 싶을 때 사용하고 @JsonName은 프로퍼티를 직렬화할 때 JSON에 넣을 때 사용되는 이름이다 그리고 마지막으로 @CustomSerialize 은 직렬화를 구현한대로 커스텀해서 사용하는 방식이다

어느정도 정리는 끝났다! 앞으로 남은건 코루틴과 async/await 항목만이 남았다!

Last updated

Was this helpful?