•
주석은 코드에 메타데이터를 첨부하는 수단이다.
annotation class Fancy
Kotlin
복사
•
주석을 선언하려면 클래스 앞에 annotation 수식어를 붙인다.
@Target(AnnotationTarget.CLASS, AnnotationTarget.FUNCTION,
AnnotationTarget.TYPE_PARAMETER, AnnotationTarget.VALUE_PARAMETER,
AnnotationTarget.EXPRESSION)
@Retention(AnnotationRetention.SOURCE)
@MustBeDocumented
annotation class Fancy
Kotlin
복사
•
주석 클래스에 메타 주석을 사용하여 추가 속성을 지정할 수 있다.
◦
@Target
▪
주석이 적용될 수 있는 요소의 종류를 지정한다.
▪
클래스, 함수, 프로퍼티, 표현식 등
◦
@Retention
▪
주석이 컴파일된 클래스 파일에 저장되는지, 런타임에 리플렉션을 통해 볼 수 있는지를 지정한다.
▪
기본값은 둘 다 true이다.
◦
@Repeatable
▪
동일한 주석을 하나의 요소에 여러 번 사용할 수 있게 한다.
◦
@MustBeDocumented
▪
주석이 공용 API의 일부로 생성된 API 문서에 클래스나 메서드 서명에 포함되어야 함을 명시한다.
사용 예시
@Fancy class Foo {
@Fancy fun baz(@Fancy foo: Int): Int {
return (@Fancy 1)
}
}
Kotlin
복사
•
클래스의 기본 생성자에 주석을 달아야 할 경우 생성자 선언에 constructor 키워드를 추가하고 주석을 그 앞에 붙인다.
class Foo @Inject constructor(dependency: MyDependency) { ... }
Kotlin
복사
•
프로퍼티 접근자에도 주석을 달 수 있다.
class Foo {
var x: MyDependency? = null
@Inject set
}
Kotlin
복사
생성자
•
주석은 매개변수를 갖는 생성자를 가질 수 있다.
annotation class Special(val why: String)
@Special("example") class Foo {}
Kotlin
복사
•
허용된 매개변수 타입
◦
Java 기본 유형(Int, Long 등)
◦
Strings
◦
Classes (Foo::class)
◦
Enums
◦
다른 annotations
◦
위 타입의 배열들
•
주석의 매개변수는 nullable 타입을 가질 수 없다.
•
JVM이 주석 속성의 값으로 null을 저장하는 것을 지원하지 않기 때문이다.
annotation class ReplaceWith(val expression: String)
annotation class Deprecated(
val message: String,
val replaceWith: ReplaceWith = ReplaceWith(""))
@Deprecated("이 함수는 더 이상 사용되지 않습니다. === 대신 사용하세요", ReplaceWith("this === other"))
Kotlin
복사
•
다른 주석의 매개변수로 주석을 사용할 경우 이름 앞에 @ 문자를 붙이지 않는다.
import kotlin.reflect.KClass
annotation class Ann(val arg1: KClass<*>, val arg2: KClass<out Any>)
@Ann(String::class, Int::class) class MyClass
Kotlin
복사
•
주석의 인자로 클래스를 지정하려면 Kotlin 클래스를 사용해야 하며 Kotlin 컴파일러가 이를 Java 클래스로 자동 변환하여 Java 코드에서 주석과 인자를 정상적으로 접근할 수 있게 한다.
인스턴스화
annotation class InfoMarker(val info: String)
fun processInfo(marker: InfoMarker): Unit = TODO()
fun main(args: Array<String>) {
if (args.isNotEmpty())
processInfo(getAnnotationReflective(args))
else
processInfo(InfoMarker("default"))
}
Kotlin
복사
•
Java에서 주석 타입은 인터페이스의 일종이므로 이를 구현하고 인스턴스를 사용할 수 있다.
•
Kotlin에서는 주석 클래스의 생성자를 임의의 코드에서 호출하고 결과 인스턴스를 사용하는 방법을 제공한다.
람다
annotation class Suspendable
val f = @Suspendable { Fiber.sleep(10) }
Kotlin
복사
•
주석은 람다에도 사용할 수 있다.
•
주석은 람다의 본체가 생성되는 invoke() 메서드에 적용된다.
•
이는 Quasar와 같은 프레임워크에서 동시성 제어를 위해 주석을 사용할 때 유용하다.
주석 사용 위치 타겟
•
사용 위치 타겟의 종류
◦
file
◦
property (이 타겟을 가진 주석은 Java에서 볼 수 없음)
◦
field
◦
get (프로퍼티 getter)
◦
set (프로퍼티 setter)
◦
receiver (확장 함수 또는 프로퍼티의 수신자 매개변수)
◦
param (생성자 매개변수)
◦
setparam (프로퍼티 setter 매개변수)
◦
delegate (위임된 프로퍼티의 delegate 인스턴스를 저장하는 필드)
•
주석 사용 위치 타겟을 지정하지 않으면 @Target 주석의 타겟 중 아래의 순서대로 타겟이 선택된다.
◦
param
◦
property
◦
field
class Example(@field:Ann val foo, // Java 필드에 주석
@get:Ann val bar, // Java getter에 주석
@param:Ann val quux) // Java 생성자 매개변수에 주석
Kotlin
복사
•
프로퍼티나 기본 생성자 매개변수를 주석으로 달 때 여러 Java 요소가 해당 Kotlin 요소에서 생성되므로 Java 바이트코드에서 주석이 생성될 수 있는 위치가 여러 개 있다.
@file:JvmName("Foo")
package org.jetbrains.demo
Kotlin
복사
•
파일 전체를 주석으로 달 때 파일이 기본 패키지인 경우 파일의 최상위에 주석을 두고 패키지 지시문이나 모든 import 문 앞에 둔다.
class Example {
@set:[Inject VisibleForTesting]
var collaborator: Collaborator
}
Kotlin
복사
•
같은 타겟을 가진 여러 개의 주석이 있는 경우 타겟을 반복하지 않도록 괄호를 추가하고 그 안에 모든 주석을 넣을 수 있다.
fun @receiver:Fancy String.myExtension() { ... }
Kotlin
복사
•
확장 함수의 수신자 매개변수에 주석을 달려면 위처럼 사용한다.
Java 주석
•
Java 주석은 Kotlin 과 100% 호환된다.
import org.junit.Test
import org.junit.Assert.*
import org.junit.Rule
import org.junit.rules.*
class Tests {
// @Rule 주석을 프로퍼티 getter에 적용
@get:Rule val tempFolder = TemporaryFolder()
@Test fun simple() {
val f = tempFolder.newFile()
assertEquals(42, getTheAnswer())
}
}
Kotlin
복사
•
Java에서 작성된 주석의 매개변수 순서가 정의되어 있지 않기 때문에 인자를 전달하기 위해 일반 함수 호출 구문을 사용할 수 없다.
// Java
public @interface Ann {
int intValue();
String stringValue();
}
// Kotlin
@Ann(intValue = 1, stringValue = "abc") class C
Kotlin
복사
•
대신 이름이 지정된 인자 구문을 사용해야 한다.
// Java
public @interface AnnWithValue {
String value();
}
// Kotlin
@AnnWithValue("abc") class C
Kotlin
복사
•
Java에서 특별한 경우로 value 매개변수가 있는 경우 명시적인 이름 없이 값을 지정할 수 있다.
// Java
public @interface AnnWithArrayValue {
String[] value();
}
// Kotlin
@AnnWithArrayValue("abc", "foo", "bar") class C
Kotlin
복사
•
Java에서 value 매개변수가 배열 타입인 경우 Kotlin 에서는 vararg 매개변수로 변환된다.
// Java
public @interface AnnWithArrayMethod {
String[] names();
}
// Kotlin
@AnnWithArrayMethod(names = ["abc", "foo", "bar"])
class C
Kotlin
복사
•
다른 인자가 배열 타입인 경우 배열 리터럴 구문이나 arrayOf(…) 를 사용해야 한다.
// Java
public @interface Ann {
int value();
}
// Kotlin
fun foo(ann: Ann) {
val i = ann.value
}
Kotlin
복사
•
주석 인스턴스의 값은 Kotlin에서 프로퍼티로 노출된다.
JVM 1.8+ 주석 타겟 생성 방지 기능
•
Kotlin 주석이 TYPE을 포함하는 경우 해당 주석은 Java 주석 타겟 목록에서 java.lang.annotation.ElementType.TYPE_USE 에 매핑된다.
•
이는 Kotlin의 TYPE_PARAMETER가 java.lang.annotation.ElementType.TYPE_PARAMETER 에 매핑되는 방식과 동일하다.
•
TYPE_USE 와 TYPE_PARAMETER 주석 타겟 생성을 피하려면 새로운 컴파일러 인자 -Xno-new-java-annotation-targets 를 사용하라.
반복 가능한 주석
•
Java와 마찬가지로 Kotlin에도 반복 가능한 주석이 있으며 이는 단일 코드 요소에 여러 번 적용될 수 있다.
•
주석을 반복 가능하게 만드려면 그 선언에 @kotlin.annotation.Repeatable 메타 주석을 붙인다.
•
이렇게 하면 Kotlin과 Java 모두에서 반복 가능해지며 Java의 반복 가능한 주석도 Kotlin 측에서 지원된다.
@Repeatable
annotation class Tag(val name: String)
// 컴파일러가 @Tag.Container 포함 주석을 생성합니다
Kotlin
복사
•
Java에서 사용되는 스킴과의 주요 차이점은 containing 주석이 없다는 것이다.
•
Kotlin 컴파일러는 자동으로 미리 정의된 이름으로 containing 주석을 생성한다.
•
@Tag 주석에 대해 @Tag.Container 라는 containing 주석이 생성된다.
@JvmRepeatable(Tags::class)
annotation class Tag(val name: String)
annotation class Tags(val value: Array<Tag>)
Kotlin
복사
•
사용자가 지정한 containing 주석 이름을 설정하려면 @kotlin.jvm.JvmRepeatable 메타 주석을 적용하고 명시적으로 선언된 containing 주석 클래스를 인자로 전달하면 된다.
KAnnotatedElement.findAnnotations()
Kotlin
복사
•
Kotlin 또는 Java 의 반복 가능한 주석을 리플렉션을 통해 추출하려면 위 함수를 사용하라.