Дорогие читатели,
Помогите сделать документацию лучше!

К сожалению текст все еще содержит некоторое количество ошибок, и мы просим вас найти и исправить их. Если бы каждый из посетителей исправил хотя бы одну ошибку, то мы смогли бы довести до совершенства весь материал всего за один день.


Аннотации

Объявление аннотаций

Аннотации являются специальной формой синтаксических метаданных, добавленных в исходный код. Для объявления аннотации используйте модификатор annotation перед именем класса:

annotation class Fancy

Дополнительные атрибуты аннотаций могут быть определены путём аннотации класса-аннотации мета-аннотациями:

  • @Target определяет возможные виды элементов, которые могут быть помечены аннотацией (классы, функции, свойства, выражения и т.д.);
  • @Retention определяет, будет ли аннотация храниться в скомпилированном классе и будет ли видима через рефлексию (по умолчанию оба утверждения верны);
  • @Repeatable позволяет использовать одну и ту же аннотацию на одном элементе несколько раз;
  • @MustBeDocumented определяет то, что аннотация является частью публичного API и должна быть включена в сигнатуру класса или метода, попадающую в сгенерированную документацию.
@Target(AnnotationTarget.CLASS, AnnotationTarget.FUNCTION,
        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). Компилятор Kotin автоматически сконвертирует его в Java класс, так что код на Java сможет видеть аннотации и их аргументы.


import kotlin.reflect.KClass

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

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

Лямбды

Аннотации также можно использовать с лямбдами. Они будут применены к 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();
}
// Kotlin
@AnnWithArrayMethod(names = arrayOf("abc", "foo", "bar")) class C

Значения экземпляра аннотации становятся свойствами в Kotlin-коде.

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