Аннотации
Аннотации — это способ прикрепления метаданных к коду. Для объявления аннотации поместите модификатор annotation перед классом:
annotation class Fancy
Дополнительные атрибуты аннотации можно указать, аннотируя класс аннотации мета-аннотациями:
@Targetуказывает возможные типы элементов, которые могут быть аннотированы данной аннотацией (например, классы, функции, свойства и выражения);@Retentionуказывает, сохраняется ли аннотация в скомпилированных файлах класса и видна ли она через рефлексию во время выполнения (по умолчанию, оба значения — true);@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
}
Повторяющиеся аннотации
Как и в 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