본문 바로가기
프로그래밍 놀이터/Kotlin, Coroutine

[Kotlin Tutorial] Annotation 과 Reflection #1 - Chap 10. Annotations and reflection

by 돼지왕 왕돼지 2017. 9. 7.
반응형

 [Kotlin Tutorial] Annotation 과 Reflection #1 - Chap 10. Annotations and reflection



참조 : Kotlin in action

Posted by 돼지왕왕돼지 ., .class, @, @deprecated, @get, @get:rule, @jsonexclude, @jsonname, @jvmfield, @JvmName, @JvmOverloads, @Jvmstatic, @RequestMapping, @retension, @Strictfp, @suppress, @Suppress annotation, @target, @Volatile, annotatio name, annotation, annotation class, annotation for annotation, annotation name, annotation named argument, annotation parameter val, annotation syntax, Annotation targets, annotationtarget, annotationtarget.property, annotation_class, Any::class, arrayof, arrays, bytecode, CLASS, class reference, Classes as annotation parameters, companion object, const modifier, constructor param, controlling how an annotation is processed, Declaring and applying annotations, Declaring annotations, default parameter, delegate, delegate instance, delegate property, enum, expression, expression annotation, extension function, extension property, field, file, function, Generic classes as annotation parameters, Get, getter, getter annotation, Java, java field, java method, java.lang.class, kclass, Kotlin, kotlin basics, kotlin tutorial, meta annotation, meta-annotation, Meta-annotations, named argument, nested object, object, out, package directive, Param, parameter, parameter annotation, Primitive type, property, property accessor, property annotation, property field, property getter, property setter, property setter parameter, public field, quick fix, receiver, receiver param, replaceWith parameter, RULE, runtime annotation, runtime retension, runtime 접근, Set, setparam, Setter, static method, string, top-level function, top-level property, type, use-site target declaration, user-site target, Using annotations to customize JSON serialization, Value, vararg, [Kotlin Tutorial] Annotation 과 Reflection #1 - Chap 10. Annotations and reflection, 기본, 코틀린, 코틀린 강좌, 코틀린 기초강좌, 코틀린 튜토리얼


10.1. Declaring and applying annotations.


10.1.1. Applying annotations


-

Annotation 사용법은 Java 와 동일하다. @ 를 prefix 로 하고 annotation name 을 써주면 된다.



-

@Deprecated 의 경우 Kotlin 은 Java 의 것에서 향상되었다.

replaceWith parameter 가 추가되어, 새로운 version 의 API 에 연결될 수 있다.

@Deprecated(“Use removeAt(index) instead.”, ReplaceWith(“removeAt(index)"))

fun remove(index: Int){ … }


이렇게 명시해주면 어떤 function 을 써줘야 함을 표기함은 물론 quick fix 도 제공된다.



-

annotation 은 parameter 로 다음 type 들만 가질 수 있다.

primitive type, string, enum, class reference, 다른 annotation, 그리고 이것들의 array



-

annotation syntax 는 java 와 약간 다르다.


클래스 이름을 명시할 때는 ::class 형태로 한다. @MyAnnotation(MyClass::class)

다른 annotation 을 argument 로 할 때는 @ 를 쓰지 않는다. 


array 를 argument 로 쓸 때는 arrayOf function 을 써야 한다. @RequestMapping(path=arrayOf(“/foo”, “/bar”)). Annotation 이 Java 에 정의되어 있다면 "value" 라는 parameter 가 자동으로 vararg 형태로 필요하면 자동으로 생성이 되기 때문에 arrayOf 없이 사용할 수도 있다.


Annotation argument 는 compile time 에 알 수 있어야 한다.

그래서 property 를 annotation argument 로 쓰려면 const modifier 를 써줘야 한다.

const val TEST_TIMEOUT = 100L // top-level 또는 object 안에 있어야 하며, primitive type 과 string 만 올 수 있다.

@Test(timeout = TEST_TIMEOUT) fun testMethod(){ … }




10.1.2. Annotation targets


-

annotation 은 use-site target declaration 으로 명시할 수 있다.

@get:Rule // @get 은 Use-site target, Rule 은 Annotation name



-

class HasTempFolder{

    @get:Rule // property 가 아닌 getter 에 annotation 이 된다.

    val folder = TemporaryFolder()


    @Test

    fun testUsingTempFolder(){

        val createdFile = folder.newFile(“myfile.txt”)

        val createFolder = folder.newFolder("subfolder”)

        ...

    }

}



-

Java 에서 정의된 annotation 을 Kotlin 의 property 에 적용하면 기본적으로 해당 field 에 적용된다고 보면 된다.

Kotlin 에서는 property 자체에 적용가능한 annotation 도 정의 가능하다.



-

use-site 를 지원하는 target 들은..


property ( Java annotation 은 적용 불가 )

field ( property 로 생성되는 field )

get ( property getter )

set ( property setter )

receiver ( extension function 이나 extension property 의 receiver param )

param ( constructor param )

setparam ( property setter parameter )

delegate ( delegate property 를 위한 delegate instance 를 저장하는 field )

file ( top-level function 이나 property 를 가진 class, package directive 전에 정의되어야 함 )



-

file 에 가장 많이 쓰이는 annotation 은 @JvmName, 이 녀석은 class 이름을 변경한다.



-

Java 와 달리 Kotlin 에서는 임의의 expression 에도 annotation 을 적용할 수 있다. ( class, function, type 뿐만 아니라 )

대표적 예는 @Suppress annotation 이다.

fun test(list: List<*>){

    @Suppress(“UNCHECKED_CAST”)

    val strings = list as List<String>

    // ...

}



-

Kotlin 에서 정의한 annotation 도 마찬가지로 bytecode 가 되어 Java 에서 볼 수 있다.

@Volatile 이나 @Strictfp 와 같은 경우는 Java 의 volatile 과 strictfp keyword (부동소수 어쩌구 저쩌구.. )로 변형된다.

다른 것들은 Kotlin 의 정의가 Java caller 에게 어떻게 보일까를 결정한다.


@JvmName 은 Kotlin 에서 정의한 Java method 나 field 이름을 변경한다.

@JvmStatic 은 object 선언이나 companion object 를 Java 에서 static method 로 보이도록 한다.

@JvmOverloads 는 Kotlin compiler 에게 default parameter 를 가진 function 을 overload 함수를 생성하도록 한다.

@JvmField 는 property 를 getter 나 setter 없이 public field 로 노출시킨다.




10.1.3. Using annotations to customize JSON serialization


-

JKid 라는 JSON serialization lib 이 있다.

data class Person(val name:String, val age: Int)


val person = Person(“Alice”, 29)

println(serialize(person)) // {“age”:29, “name”: “Alice” }


val json = “””{“name”:”Alice”, “age”: 29}”””

println(deserialize<Person>(json)) // Person(name=Alice, age=29)


serialize 를 할 때 기본적으로 모든 property 를 그 이름 자체로 key 로 해서 serialize 한다.

@JsonExclue, @JsonName 을 통해 exclude 를 할 수도 있고, 이름을 바꿀 수도 있다.




10.1.4. Declaring annotations


-

annotation class JsonExclude


syntax 는 class 정의와 비슷하나, annotation 이 앞에 붙는다.



-

annotation class JsonName(val name:String)


parameter 가 있는 annotation.

parameter 는 필수적으로 val 이다.


Java 코드는 아래와 같다.

public @interface JsonName{

    String value();

}


여기서 value 는 그냥 1개의 param 을 받는다는 의미이고, 만약에 다른 param 을 추가하려면 해당 getter 를 추가로 써줘야 한다.



-

Kotlin 에서의 annotation 은 named-argument 를 쓸 수 있다.

@JsonName(name=“first_name”)

@JsonName(“first_name”)


Java 에서 정의한 annotation 을 사용할 때는 value 를 제외한 모든 경우 named-argument 를 사용해야 한다.




10.1.5. Meta-annotations: controlling how an annotation is processed


-

Java 처럼 Kotlin annotation 은 그 자신을 annotation 할 수 있다.

annotation 에 annotation 되는 class 를 meta-annotations 라고 부른다.



-

standard lib 에 정의된 가장 잘 쓰이는 녀석은 @Target 이다.

@Target(AnnotationTarget.PROPERTY)

annotation class JsonExclue


@Target 은 annotation 이 어디에 적용될 수 있는지 명시하는 것이다.

명시되지 않으면 모든 target 에 적용될 수 있다.


Target 으로 지정할 수 있는 enum 은..

class, file, function, property, property accessor, type, expression 등이다.

, 를 통해 여러개의 target 을 지정할 수도 있다.

@Target(AnnotationTarget.CLASS, AnnotationTarget.METHOD)



-

meta-annotation 을 만들기 위해서는 target 을  ANNOTATION_CLASS 로 지정해줘야 한다.

@Target(AnnotationTarget.ANNOTATION_CLASS)

annotation class BindingAnnotation


@BindingAnnotation

annotation class MyBinding



-

Property target 을 하고 있는 annotation 은 Java 코드에서 사용할 수 없다.

Java 에도 사용하게 하려면 FIELD target 도 추가해주어야 한다.



-

Java 는 기본적으로 annotation 을 .class files 로 만들지만, runtime 에는 접근 가능하지 않게 한다.

@Retension annotation 을 사용하면 .class 로 만들지, runtime 에도 접근하게 할지를 control 할 수 있다.


Kotlin 에서는 기본이 RUNTIME retension 이다.




10.1.6. Classes as annotation parameters


-

annotation 에 class reference 를 넣을 수도 있다.

annotation class DeserializeInterface(val targetClass: KClass<out Any>)

// 여기서 out 이 없다면 Any::class 밖에 안 된다.


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)




-

KClass 는 Java 의 java.lang.Class type 과 매칭되는 녀석이다.




10.1.7. Generic classes as annotation parameters


-

Jkid 는 primitive type 이 아닌 property 들은 nested object 로 serialize 한다.

이 로직을 @CustomSerializer annotation 을 써서 변경할 수 있다.

interface ValueSerializer<T>{

    fun toJsonValue(value: T): Any?

    fun fromJsonValue(jsonValue: Any?) : T

}


annotation class CustomSerializer{

    val serializerClass: KClass<out ValueSerializer<*>>

}


data class Person{

    val name: String,

    @CustomSerializer(DateSerializer::class) val birthDate: Date

}





반응형

댓글