type | layout | category | title | url |
---|---|---|---|---|
doc |
reference |
Syntax |
Аннотации |
Аннотации являются специальной формой синтаксических метаданных, добавленных в исходный код. Для объявления аннотации используйте модификатор 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-аннотации на 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 1.2+:
@AnnWithArrayMethod(names = ["abc", "foo", "bar"]) class C
// 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
}