Hanbit the Developer

Kotlin Documentation | Annotations 본문

Kotlin

Kotlin Documentation | Annotations

hanbikan 2023. 5. 22. 16:33

Kotlin Documentation 시리즈에 대해

Category: Concepts

문서 링크: https://kotlinlang.org/docs/annotations.html


어노테이션은 메타 데이터를 코드에 붙일 수 있는 수단이다. 어노테이션을 정의하기 위해 annotation 구분자를 클래스 앞에 붙인다.

annotation class Fancy

Additional attributes of the annotation:

  • @Target: 어떤 유형에 어노테이션이 붙을 수 있는지를 명시한다.(클래스, 함수, 프로퍼티, 표현식)
  • @Retention: 주석이 컴파일된 클래스 파일에 저장되는지 여부, 런타임에 리플랙션을 통해 표시되어야 하는지 여부를 결정한다.(기본적으로 양쪽 모두 참임)
  • @Repeatable: 하나의 원소에 같은 어노테이션이 여러 번 붙을 수 있게 허용한다.
  • @MustBeDocumented: 어노테이션이 공개 API의 일부이며 생성된 API 문서에 표시된 클래스나 메서드에 포함되어야 한다고 지정한다.
@Target(AnnotationTarget.CLASS, AnnotationTarget.FUNCTION,
        AnnotationTarget.TYPE_PARAMETER, AnnotationTarget.VALUE_PARAMETER,
        AnnotationTarget.EXPRESSION)
@Retention(AnnotationRetention.SOURCE)
@MustBeDocumented
annotation class Fancy

Usage

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

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

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

Constructors

annotation class Special(**val why: String**)

@Special(**"example"**) class Foo {}

인자 타입:

  • Primitive types(Int, Long etc.)
  • Strings
  • Classes(Foo::class, 인자 선언은 KClass로 한다. 예시: val arg: KClass<*>)
  • Enums
  • Other annotations: 인자로 쓰일 땔 앞에 @가 붙지 않음
  • 위 타입들을 감싸고 있는 Arrays

*non-nullable

Instantiation

어노테이션은 인터페이스의 형태를 하기 때문에 인스턴스를 생성할 수 있다.

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

Lambdas

람다에 붙어서 invoke() 메소드에 적용될 수 있다.

annotation class Suspendable

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

Annotation use-site targets

Annotation이 생성되는 정확한 방법을 명시하려면 다음과 같이 타겟을 명시해야 한다:

@Target(
    AnnotationTarget.FIELD,
    AnnotationTarget.VALUE_PARAMETER,
    AnnotationTarget.PROPERTY_GETTER
)
annotation class Ann

class Example(
    @Ann val hbk: String,           // without target
    @field:Ann val foo: String,     // annotate Java field
    @get:Ann val bar: String,       // annotate Java getter
    @param:Ann val quux: String     // annotate Java constructor parameter
)

위 코틀린 코드를 Java로 디컴파일한 내용은 다음과 같다:

public final class Example {
   @NotNull
   private final String hbk;
   @Ann
   @NotNull
   private final String foo;
   @NotNull
   private final String bar;
   @NotNull
   private final String quux;

   @NotNull
   public final String getHbk() {
      return this.hbk;
   }

   @NotNull
   public final String getFoo() {
      return this.foo;
   }

   @Ann
   @NotNull
   public final String getBar() {
      return this.bar;
   }

   @NotNull
   public final String getQuux() {
      return this.quux;
   }

   public Example(@Ann @NotNull String hbk, @NotNull String foo, @NotNull String bar, @Ann @NotNull String quux) {
      Intrinsics.checkNotNullParameter(hbk, "hbk");
      Intrinsics.checkNotNullParameter(foo, "foo");
      Intrinsics.checkNotNullParameter(bar, "bar");
      Intrinsics.checkNotNullParameter(quux, "quux");
      super();
      this.hbk = hbk;
      this.foo = foo;
      this.bar = bar;
      this.quux = quux;
   }
}

*@Ann이 어디에 부착되었는지 확인할 것. 타겟을 명시하지 않은 것이 FIELD로 지정된 이유는, Ann의 타겟 정의 부분에서 첫번째 아이템이 AnnotationTarget.FIELD이기 때문이다.

@file:JvmName("Foo")

package org.jetbrains.demo

같은 타겟의 여러 어노테이션이 있는 경우 다음과 같이 표기할 수 있다.

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

타겟 리스트는 다음과 같다.

  • file
  • property
  • field
  • get
  • set
  • receiver(receiver parameter of an extension function or property)
  • fun @receiver:Fancy String.myExtension() { ... }
  • param(constructor parameter)
  • setparam(property setter parameter)
  • delegate(the field storing the delegate instance for a delegated property)

타겟을 명시하지 않는 경우, 사용 중인 어노테이션의 @Target 어노테이션에 따라 대상이 결정된다. 만약 @Target에 여러 타겟이 명시되어 있을 경우 다음 리스트에서 첫번째로 적용 가능한 타겟이 사용된다:

  • param
  • property
  • field

Java annotations

named argument syntax:

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

value 인자라는 특별한 경우에서는 이름을 명시하지 않아도 된다.

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

Arrays as annotation parameters

value가 배열인 경우, 코틀린에서 vararg로 변환된다.

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

일반적인 경우:

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

Accessing properties of an annotation instance

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

Repeatable annotations