Аннотации
Аннотации — это способ добавления метаданных к коду. Чтобы объявить аннотацию, поместите модификатор 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
}
Полный список поддерживаемых целей применения аннотаций:
fileproperty(аннотации с этой целью не видны для Java)fieldget(получатель свойства)set(установщик свойства)receiver(параметр получателя расширяющей функции или свойства)param(параметр конструктора)setparam(параметр установщика свойства)delegate(поле, хранящее экземпляр делегата для делегированного свойства)
Чтобы аннотировать параметр получателя расширяющей функции, используйте следующий синтаксис:
fun @receiver:Fancy String.myExtension() { ... }
Если вы не указываете цель применения аннотации, цель выбирается в соответствии с @Target аннотацией используемой аннотации. Если имеется несколько соответствующих целей, используется первая соответствующая цель из следующего списка:
parampropertyfield
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.
© 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