Spec-Zone.ru › Kotlin 1.9

Аннотации

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

annotation class Fancy

Дополнительные атрибуты аннотации можно указать, аннотируя класс аннотации мета-аннотациями:

  • @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

Использование

@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)

  • Перечисления

  • Другие аннотации

  • Массивы перечисленных выше типов

Параметры аннотации не могут иметь типы с возможностью null, потому что JVM не поддерживает хранение null в качестве значения атрибута аннотации.

Если аннотация используется как параметр другой аннотации, её имя не предваряется символом @.

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, которые используют аннотации для управления параллельностью.

annotation class Suspendable

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

Цели применения аннотаций

При аннотировании свойства или параметра первичного конструктора существует несколько Java-элементов, сгенерированных из соответствующего Kotlin-элемента, и, следовательно, несколько возможных мест для аннотации в сгенерированном Java-байткоде. Чтобы указать, как именно должна быть сгенерирована аннотация, используйте следующий синтаксис:

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

Тот же синтаксис можно использовать для аннотирования всего файла. Для этого поместите аннотацию с целью 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-аннотации полностью совместимы с Kotlin.

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

class Tests {
    // apply @Rule annotation to property getter
    @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 имеет тип массива, он становится параметром vararg в Kotlin:

// 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-аннотация имеет TYPE среди своих Kotlin-целей, аннотация сопоставляется с java.lang.annotation.ElementType.TYPE_USE в своём списке Java-целей аннотаций. Это аналогично тому, как TYPE_PARAMETER Kotlin-цель сопоставляется с java.lang.annotation.ElementType.TYPE_PARAMETER Java-целью. Это проблема для Android-клиентов с уровнями API ниже 26, которые не имеют этих целей в API.

Чтобы избежать генерации целей аннотации TYPE_USE и TYPE_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)

// The compiler generates the @Tag.Container containing annotation

Вы можете установить пользовательское имя для содержащей аннотации, применив мета-аннотацию @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.

Последнее изменение: 28 декабря 2022 г.
Корутины Деструктурирующие объявления

© 2010–2023 JetBrains s.r.o. and Kotlin Programming Language contributors
Licensed under the Apache License, Version 2.0.
https://kotlinlang.org/docs/annotations.html

Spec-Zone.ru

Настройки Оффлайн Что нового Помощь О нас
Spec-Zone .ru
спецификации, руководства, описания, API