본문으로 건너뛰기

어노테이션

어노테이션은 코드에 메타데이터를 첨부하는 수단입니다. 어노테이션을 선언하려면 클래스 앞에 annotation 변경자를 넣습니다.

annotation class Fancy

메타 어노테이션으로 어노테이션 클래스를 어노테이팅하여 어노테이션의 추가 속성을 지정할 수 있습니다.

  • @Target은 (클래스, 함수, 프로퍼티, 표현식 등) 어노테이션으로 어노테이팅할 수 있는 요소의 종류를 지정합니다.
  • @Retention은 어노테이션이 컴파일된 클래스 파일에 저장되는지 여부와 런타임 시 리플렉션을 통해 보이는지 여부를 지정합니다 (기본적으로 둘 다 true).
  • @Repeatable은 단일 요소에 동일한 어노테이션을 여러 번 사용할 수 있도록 허용합니다.
  • @MustBeDocumented는 어노테이션이 공개 API의 일부이며 생성된 API 문서에 표시되는 클래스 또는 메서드 시그니처에 포함되어야 함을 지정합니다.
@Target(AnnotationTarget.CLASS, AnnotationTarget.FUNCTION,
AnnotationTarget.TYPE_PARAMETER, AnnotationTarget.VALUE_PARAMETER,
AnnotationTarget.EXPRESSION)
@Retention(AnnotationRetention.SOURCE)
@MustBeDocumented
annotation class Fancy

사용법

@Fancy class Foo {
@Fancy fun baz(@Fancy foo: Int): Int {
return (@Fancy 1)
}
}

클래스의 주 생성자에 어노테이팅해야 하는 경우 생성자 선언에 constructor 키워드를 추가하고 그 앞에 어노테이션을 추가해야 합니다.

class Foo @Inject constructor(dependency: MyDependency) { ... }

프로퍼티 접근자에 어노테이팅할 수도 있습니다.

class Foo {
var x: MyDependency? = null
@Inject set
}

생성자

어노테이션은 파라미터를 사용하는 생성자를 가질 수 있습니다.

annotation class Special(val why: String)

@Special("example") class Foo {}

허용되는 파라미터 유형은 다음과 같습니다.

  • Java 기본 유형 (Int, Long 등)에 해당하는 유형
  • 문자열
  • 클래스 (Foo::class)
  • Enum
  • 기타 어노테이션
  • 위에 나열된 유형의 배열

JVM은 null을 어노테이션 속성 값으로 저장하는 것을 지원하지 않기 때문에 어노테이션 파라미터는 nullable 유형을 가질 수 없습니다.

어노테이션이 다른 어노테이션의 파라미터로 사용되는 경우 해당 이름에는 @ 문자가 접두사로 붙지 않습니다.

annotation class ReplaceWith(val expression: String)

annotation class Deprecated(
val message: String,
val replaceWith: ReplaceWith = ReplaceWith(""))

@Deprecated("This function is deprecated, use === instead", ReplaceWith("this === other"))

클래스를 어노테이션의 인수로 지정해야 하는 경우 Kotlin 클래스 (KClass)를 사용하십시오. Kotlin 컴파일러는 자동으로 Java 클래스로 변환하므로 Java 코드는 어노테이션과 인수에 정상적으로 액세스할 수 있습니다.


import kotlin.reflect.KClass

annotation class Ann(val arg1: KClass<*>, val arg2: KClass<out Any>)

@Ann(String::class, Int::class) class MyClass

인스턴스화

Java에서 어노테이션 유형은 인터페이스의 한 형태이므로 이를 구현하고 인스턴스를 사용할 수 있습니다. 이 메커니즘에 대한 대안으로 Kotlin에서는 임의의 코드에서 어노테이션 클래스의 생성자를 호출하고 결과 인스턴스를 유사하게 사용할 수 있습니다.

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"))
}

어노테이션 클래스의 인스턴스화에 대한 자세한 내용은 이 KEEP에서 확인하십시오.

람다

어노테이션은 람다에서도 사용할 수 있습니다. 람다는 람다 본문이 생성되는 invoke() 메서드에 적용됩니다. 이는 Quasar와 같은 프레임워크에 유용합니다. Quasar는 동시성 제어를 위해 어노테이션을 사용합니다.

annotation class Suspendable

val f = @Suspendable { Fiber.sleep(10) }

어노테이션 사용 위치 대상

프로퍼티 또는 주 생성자 파라미터를 어노테이팅할 때 해당 Kotlin 요소에서 생성된 여러 Java 요소가 있으므로 생성된 Java 바이트코드에서 어노테이션을 배치할 수 있는 위치가 여러 곳일 수 있습니다. 어노테이션을 생성하는 정확한 방법을 지정하려면 다음 구문을 사용하십시오.

class Example(@field:Ann val foo,    // Java 필드에 어노테이팅
@get:Ann val bar, // Java getter에 어노테이팅
@param:Ann val quux) // Java 생성자 파라미터에 어노테이팅

동일한 구문을 사용하여 전체 파일에 어노테이팅할 수 있습니다. 이렇게 하려면 file 대상이 있는 어노테이션을 파일의 최상위 수준에 패키지 지시문 앞이나 파일이 기본 패키지에 있는 경우 모든 import문 앞에 넣으십시오.

@file:JvmName("Foo")

package org.jetbrains.demo

동일한 대상이 있는 여러 어노테이션이 있는 경우 대상 뒤에 대괄호를 추가하고 대괄호 안에 모든 어노테이션을 넣어 대상을 반복하는 것을 피할 수 있습니다.

class Example {
@set:[Inject VisibleForTesting]
var collaborator: Collaborator
}

지원되는 사용 위치 대상의 전체 목록은 다음과 같습니다.

  • file
  • property (이 대상이 있는 어노테이션은 Java에 표시되지 않습니다.)
  • field
  • get (프로퍼티 getter)
  • set (프로퍼티 setter)
  • receiver (확장 함수 또는 프로퍼티의 receiver 파라미터)
  • param (생성자 파라미터)
  • setparam (프로퍼티 setter 파라미터)
  • delegate (위임된 프로퍼티에 대한 delegate 인스턴스를 저장하는 필드)

확장 함수의 receiver 파라미터를 어노테이팅하려면 다음 구문을 사용하십시오.

fun @receiver:Fancy String.myExtension() { ... }

사용 위치 대상을 지정하지 않으면 사용되는 어노테이션의 @Target 어노테이션에 따라 대상이 선택됩니다. 적용 가능한 대상이 여러 개인 경우 다음 목록에서 첫 번째 적용 가능한 대상이 사용됩니다.

  • param
  • property
  • field

Java 어노테이션

Java 어노테이션은 Kotlin과 100% 호환됩니다.

import org.junit.Test
import org.junit.Assert.*
import org.junit.Rule
import org.junit.rules.*

class Tests {
// 프로퍼티 getter에 @Rule 어노테이션 적용
@get:Rule val tempFolder = TemporaryFolder()

@Test fun simple() {
val f = tempFolder.newFile()
assertEquals(42, getTheAnswer())
}
}

Java로 작성된 어노테이션의 파라미터 순서가 정의되어 있지 않으므로 인수를 전달하는 데 일반 함수 호출 구문을 사용할 수 없습니다. 대신 이름이 지정된 인수 구문을 사용해야 합니다.

// Java
public @interface Ann {
int intValue();
String stringValue();
}
// Kotlin
@Ann(intValue = 1, stringValue = "abc") class C

Java와 마찬가지로 특별한 경우는 value 파라미터입니다. 값은 명시적 이름 없이 지정할 수 있습니다.

// Java
public @interface AnnWithValue {
String value();
}
// Kotlin
@AnnWithValue("abc") class C

어노테이션 파라미터로 배열 사용

Java에서 value 인수가 배열 유형인 경우 Kotlin에서 vararg 파라미터가 됩니다.

// Java
public @interface AnnWithArrayValue {
String[] value();
}
// Kotlin
@AnnWithArrayValue("abc", "foo", "bar") class C

배열 유형인 다른 인수의 경우 배열 리터럴 구문 또는 arrayOf(...)를 사용해야 합니다.

// Java
public @interface AnnWithArrayMethod {
String[] names();
}
@AnnWithArrayMethod(names = ["abc", "foo", "bar"]) 
class C

어노테이션 인스턴스의 프로퍼티 액세스

어노테이션 인스턴스의 값은 Kotlin 코드에 프로퍼티로 노출됩니다.

// Java
public @interface Ann {
int value();
}
// Kotlin
fun foo(ann: Ann) {
val i = ann.value
}

JVM 1.8+ 어노테이션 대상 생성 기능 비활성화

Kotlin 어노테이션에 Kotlin 대상 중 TYPE이 있는 경우 어노테이션은 Java 어노테이션 대상 목록에서 java.lang.annotation.ElementType.TYPE_USE에 매핑됩니다. 이는 TYPE_PARAMETER Kotlin 대상이 java.lang.annotation.ElementType.TYPE_PARAMETER Java 대상에 매핑되는 방식과 같습니다. 이는 API 수준이 26 미만인 Android 클라이언트에 문제가 됩니다. 해당 클라이언트는 API에 이러한 대상이 없기 때문입니다.

TYPE_USETYPE_PARAMETER 어노테이션 대상 생성을 피하려면 새 컴파일러 인수 -Xno-new-java-annotation-targets를 사용하십시오.

반복 가능한 어노테이션

Java에서와 같이, Kotlin에는 반복 가능한 어노테이션이 있으며 단일 코드 요소에 여러 번 적용할 수 있습니다. 어노테이션을 반복 가능하게 만들려면 선언을 @kotlin.annotation.Repeatable 메타 어노테이션으로 표시하십시오. 이렇게 하면 Kotlin과 Java 모두에서 반복 가능해집니다. Java 반복 가능한 어노테이션도 Kotlin 측에서 지원됩니다.

Java에서 사용되는 체계와의 주요 차이점은 Kotlin 컴파일러가 사전 정의된 이름으로 자동 생성하는 _포함 어노테이션_이 없다는 것입니다. 아래 예제의 어노테이션의 경우 컴파일러는 포함 어노테이션 @Tag.Container를 생성합니다.

@Repeatable
annotation class Tag(val name: String)

// 컴파일러는 @Tag.Container 포함 어노테이션을 생성합니다.

@kotlin.jvm.JvmRepeatable 메타 어노테이션을 적용하고 명시적으로 선언된 포함 어노테이션 클래스를 인수로 전달하여 포함 어노테이션에 대한 사용자 지정 이름을 설정할 수 있습니다.

@JvmRepeatable(Tags::class)
annotation class Tag(val name: String)

annotation class Tags(val value: Array<Tag>)

리플렉션을 통해 Kotlin 또는 Java 반복 가능한 어노테이션을 추출하려면 KAnnotatedElement.findAnnotations() 함수를 사용하십시오.

Kotlin 반복 가능한 어노테이션에 대한 자세한 내용은 이 KEEP에서 확인하십시오.