Аннотации
Аннотации являются специальной формой синтаксических метаданных, добавленных в исходный код.
Для объявления аннотации используйте модификатор annotation перед именем класса.
annotation class Fancy
Дополнительные атрибуты аннотаций могут быть определены путём аннотации класса-аннотации мета-аннотациями:
- @Targetопределяет возможные виды элементов, которые могут быть помечены аннотацией (такие как классы, функции, свойства и выражения);
- @Retentionопределяет, будет ли аннотация храниться в скомпилированном классе и будет ли видима через рефлексию (по умолчанию оба утверждения верны);
- @Repeatableпозволяет использовать одну и ту же аннотацию на одном элементе несколько раз;
- @MustBeDocumentedопределяет то, что аннотация является частью публичного 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("пример") class Foo {}
Разрешены параметры следующих типов:
- типы, которые соответствуют примитивам Java (Int, Long и т.д.),
- строки,
- классы (Foo::class),
- перечисляемые типы,
- другие аннотации,
- массивы, содержащие значения приведённых выше типов.
Параметры аннотаций не могут иметь обнуляемые типы, потому что JVM не поддерживает хранение null в качестве значения
атрибута аннотации.
Если аннотация используется в качестве параметра к другой аннотации, её имя не нужно начинать со знака @.
annotation class ReplaceWith(val expression: String)
annotation class Deprecated(
        val message: String,
        val replaceWith: ReplaceWith = ReplaceWith(""))
@Deprecated("Эта функция устарела, вместо неё используйте ===", 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"))
}
Создание экземпляров классов аннотаций для Kotlin/Native пока недоступно.
Узнайте больше о создании экземпляров классов аннотаций в этом KEEP.
Лямбды
Аннотации также можно использовать с лямбдами. Они будут применены к invoke() методу, в который генерируется тело
лямбды. Это полезно для фреймворков вроде Quasar, которые используют
аннотации для контроля многопоточности.
annotation class Suspendable
val f = @Suspendable { Fiber.sleep(10) }
Аннотации с указаниями
Когда вы помечаете свойство или первичный конструктор аннотацией, из соответствующего Kotlin-элемента генерируются несколько Java-элементов, и поэтому в сгенерированном байт-коде элемент появляется в нескольких местах. Чтобы указать, в каком именно месте аннотация должна быть сгенерирована, используйте следующий синтаксис:
class Example(@field:Ann val foo,  // аннотация для Java-поля
              @get:Ann val bar,    // аннотация для Java-геттера
              @param:Ann val quux) // аннотация для параметра конструктора Java 
Тот же синтаксис может быть использован для аннотации целого файла. Для этого отметьте аннотацию словом file и
вставьте её в начале файла: перед указанием пакета или перед импортами, если файл находится в пакете по умолчанию.
@file:JvmName("Foo")
package org.jetbrains.demo
Если вы помечаете несколькими аннотациями одно указание, вы можете избежать повторения путём указания всех аннотаций в квадратных скобках.
class Example {
     @set:[Inject VisibleForTesting]
     var collaborator: Collaborator
}
Полный список поддерживаемых указаний:
- file
- property(такие аннотации не будут видны в Java)
- field
- get(геттер)
- set(сеттер)
- receiver(параметр-приёмник расширения)
- param(параметр конструктора)
- setparam(параметр сеттера)
- delegate(поле, которое хранит экземпляр делегата для делегированного свойства)
Чтобы пометить аннотацией параметр-приёмник расширения, используйте следующий синтаксис:
fun @receiver:Fancy String.myExtension() { /* ... */ }
Если вы не сделали указание, аннотация будет применена к элементу, выбранному в соответствии с аннотацией @Target той
аннотации, которую вы используете. Если существует несколько элементов, к которым возможно применение аннотации, будет
выбран первый подходящий элемент из следующего списка:
- param
- property
- field
Java-аннотации
Java-аннотации на 100% совместимы в Kotlin:
import org.junit.Test
import org.junit.Assert.*
import org.junit.Rule
import org.junit.rules.*
class Tests {
    // применение аннотации @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
Массивы в качестве параметров аннотаций
Если аргумент value в Java является массивом, в 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
}
Повторяющиеся аннотации
Как и в 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.