Рефлексия
Рефлексия — это набор языковых возможностей и функций библиотеки, позволяющих исследовать структуру программы во время выполнения. Функции и свойства в Kotlin являются объектами первого класса, и возможность исследовать их (например, узнать имя или тип свойства либо функции во время выполнения) необходима при использовании функционального или реактивного стиля.
Зависимость для JVM
Для платформы JVM дистрибутив компилятора Kotlin содержит компонент среды выполнения, необходимый для использования возможностей рефлексии, в виде отдельного артефакта — kotlin-reflect.jar. Это сделано для уменьшения размера библиотеки среды выполнения, необходимой приложениям, которые не используют возможности рефлексии.
Чтобы использовать рефлексию в проекте Gradle или Maven, добавьте зависимость от kotlin-reflect:
-
В Gradle:
dependencies { implementation(kotlin("reflect")) }dependencies { implementation "org.jetbrains.kotlin:kotlin-reflect:2.4.20" } -
В Maven:
<dependencies> <dependency> <groupId>org.jetbrains.kotlin</groupId> <artifactId>kotlin-reflect</artifactId> </dependency> </dependencies>
Если вы не используете Gradle или Maven, убедитесь, что в пути к классам вашего проекта есть kotlin-reflect.jar. В других поддерживаемых случаях (проекты IntelliJ IDEA, использующие компилятор командной строки) он добавляется по умолчанию. В компиляторе командной строки можно использовать параметр компилятора -no-reflect, чтобы исключить kotlin-reflect.jar из пути к классам.
Ссылки на классы
Самая базовая возможность рефлексии — получение ссылки на класс Kotlin во время выполнения. Чтобы получить ссылку на статически известный класс Kotlin, можно использовать синтаксис литерала класса:
val c = MyClass::class
Ссылка представляет собой значение типа KClass.
Связанные ссылки на классы
Получить ссылку на класс конкретного объекта можно с помощью того же синтаксиса ::class, используя объект в качестве получателя:
val widget: Widget = ...
assert(widget is GoodWidget) { "Bad widget: ${widget::class.qualifiedName}" }
Вы получите ссылку на точный класс объекта, например GoodWidget или BadWidget, независимо от типа выражения-получателя (Widget).
Ссылки на вызываемые элементы
Ссылки на функции, свойства и конструкторы также можно вызывать или использовать как экземпляры функциональных типов.
Общим суперклассом всех ссылок на вызываемые элементы является KCallable<out R>, где R — тип возвращаемого значения. Для свойств это тип свойства, а для конструкторов — тип создаваемого объекта.
Ссылки на функции
Если у вас есть именованная функция, объявленная, как показано ниже, вы можете вызвать её напрямую (isOdd(5)):
fun isOdd(x: Int) = x % 2 != 0
Кроме того, функцию можно использовать как значение функционального типа, то есть передать другой функции. Для этого используйте оператор :::
fun isOdd(x: Int) = x % 2 != 0
fun main() {
//sampleStart
val numbers = listOf(1, 2, 3)
println(numbers.filter(::isOdd))
//sampleEnd
}
Здесь ::isOdd — значение функционального типа (Int) -> Boolean.
Ссылки на функции относятся к одному из подтипов KFunction<out R> в зависимости от количества параметров. Например, KFunction3<T1, T2, T3, R>.
:: можно использовать с перегруженными функциями, если ожидаемый тип известен из контекста. Например:
fun main() {
//sampleStart
fun isOdd(x: Int) = x % 2 != 0
fun isOdd(s: String) = s == "brillig" || s == "slithy" || s == "tove"
val numbers = listOf(1, 2, 3)
println(numbers.filter(::isOdd)) // refers to isOdd(x: Int)
//sampleEnd
}
Кроме того, необходимый контекст можно задать, сохранив ссылку на метод в переменной с явно указанным типом:
val predicate: (String) -> Boolean = ::isOdd // refers to isOdd(x: String)
Если нужно использовать член класса или функцию-расширение, ссылку необходимо квалифицировать: String::toCharArray.
Даже если инициализировать переменную ссылкой на функцию-расширение, выведенный функциональный тип не будет иметь получателя, но будет содержать дополнительный параметр, принимающий объект-получатель. Чтобы получить функциональный тип с получателем, укажите тип явно:
val isEmptyStringList: List<String>.() -> Boolean = List<String>::isEmpty
Пример: композиция функций
Рассмотрим следующую функцию:
fun <A, B, C> compose(f: (B) -> C, g: (A) -> B): (A) -> C {
return { x -> f(g(x)) }
}
Она возвращает композицию двух переданных ей функций: compose(f, g) = f(g(*)). Эту функцию можно применить к ссылкам на вызываемые элементы:
fun <A, B, C> compose(f: (B) -> C, g: (A) -> B): (A) -> C {
return { x -> f(g(x)) }
}
fun isOdd(x: Int) = x % 2 != 0
fun main() {
//sampleStart
fun length(s: String) = s.length
val oddLength = compose(::isOdd, ::length)
val strings = listOf("a", "ab", "abc")
println(strings.filter(oddLength))
//sampleEnd
}
Ссылки на свойства
Чтобы обращаться к свойствам как к объектам первого класса в Kotlin, используйте оператор :::
val x = 1
fun main() {
println(::x.get())
println(::x.name)
}
Выражение ::x вычисляется в объект свойства типа KProperty0<Int>. Его значение можно прочитать с помощью get() или получить имя свойства с помощью свойства name. Подробнее см. в документации по классу KProperty.
Для изменяемого свойства, например var y = 1, ::y возвращает значение типа KMutableProperty0<Int>, у которого есть метод set():
var y = 1
fun main() {
::y.set(2)
println(y)
}
Ссылку на свойство можно использовать там, где ожидается функция с одним обобщённым параметром:
fun main() {
//sampleStart
val strs = listOf("a", "bc", "def")
println(strs.map(String::length))
//sampleEnd
}
Чтобы обратиться к свойству, являющемуся членом класса, укажите его квалификатором следующим образом:
fun main() {
//sampleStart
class A(val p: Int)
val prop = A::p
println(prop.get(A(1)))
//sampleEnd
}
Для свойства-расширения:
val String.lastChar: Char
get() = this[length - 1]
fun main() {
println(String::lastChar.get("abc"))
}
Взаимодействие с рефлексией Java
Для платформы JVM стандартная библиотека содержит расширения для классов рефлексии, обеспечивающие преобразование объектов рефлексии Java в объекты Kotlin и обратно (см. пакет kotlin.reflect.jvm). Например, чтобы найти поле хранения или метод Java, выполняющий роль геттера для свойства Kotlin, можно написать что-то вроде следующего:
import kotlin.reflect.jvm.*
class A(val p: Int)
fun main() {
println(A::p.javaGetter) // prints "public final int A.getP()"
println(A::p.javaField) // prints "private final int A.p"
}
Чтобы получить класс Kotlin, соответствующий классу Java, используйте свойство-расширение .kotlin:
fun getKClass(o: Any): KClass<Any> = o.javaClass.kotlin
Ссылки на конструкторы
На конструкторы можно ссылаться так же, как на методы и свойства. Их можно использовать везде, где программа ожидает объект функционального типа, принимающий те же параметры, что и конструктор, и возвращающий объект соответствующего типа. Ссылка на конструктор задаётся с помощью оператора :: и имени класса. Рассмотрим следующую функцию, которая принимает параметр-функцию без параметров и с возвращаемым типом Foo:
class Foo
fun function(factory: () -> Foo) {
val x: Foo = factory()
}
Используя ::Foo — конструктор класса Foo без аргументов, — его можно вызвать так:
function(::Foo)
Ссылки на конструкторы типизируются одним из подтипов KFunction<out R> в зависимости от количества параметров.
Связанные ссылки на функции и свойства
Можно сослаться на метод конкретного объекта:
fun main() {
//sampleStart
val numberRegex = "\\d+".toRegex()
println(numberRegex.matches("29"))
val isNumber = numberRegex::matches
println(isNumber("29"))
//sampleEnd
}
Вместо прямого вызова метода matches в примере используется ссылка на него. Такая ссылка привязана к своему получателю. Её можно вызвать напрямую (как в примере выше) или использовать везде, где ожидается выражение функционального типа:
fun main() {
//sampleStart
val numberRegex = "\\d+".toRegex()
val strings = listOf("abc", "124", "a70")
println(strings.filter(numberRegex::matches))
//sampleEnd
}
Сравните типы связанных и несвязанных ссылок. У связанной ссылки на вызываемый элемент получатель «прикреплён» к ней, поэтому тип получателя больше не является параметром:
val isNumber: (CharSequence) -> Boolean = numberRegex::matches val matches: (Regex, CharSequence) -> Boolean = Regex::matches
Ссылку на свойство тоже можно связать:
fun main() {
//sampleStart
val prop = "abc"::length
println(prop.get())
//sampleEnd
}
Указывать this в качестве получателя необязательно: this::foo и ::foo эквивалентны.
Связанные ссылки на конструкторы
Связанную ссылку на вызываемый элемент — конструктор внутреннего класса — можно получить, передав экземпляр внешнего класса:
class Outer {
inner class Inner
}
val o = Outer()
val boundInnerCtor = o::Inner
© 2010–2026 JetBrains s.r.o. and Kotlin Programming Language contributors
Licensed under the Apache License, Version 2.0.
https://kotlinlang.org/docs/reflection.html