Аннотации
Аннотации — это способ прикрепления метаданных к коду. Чтобы объявить аннотацию, поместите модификатор 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 на 100% совместимы с 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
}
Повторяющиеся аннотации
Как и в 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–2022 JetBrains s.r.o. and Kotlin Programming Language contributors
Licensed under the Apache License, Version 2.0.
https://kotlinlang.org/docs/annotations.html