Kotlin Help

Reflection

Reflection is a set of language and library features that allows you to introspect the structure of your program at runtime. For example, you can read or update a property, call a function, or invoke a class constructor to create an instance. This is helpful when you don't know the declarations at compile time.

Reflection provides runtime objects that describe compiled declarations. For example, a class is represented by KClass, a type by KType, and a property or function by a subtype of KCallable. You can inspect these objects and use them to access a value or invoke a function.

How reflection works

The reflection API represents compiled declarations through Kotlin types such as KClass or KFunction. A reflection object represents the declaration itself, not the value of the declared object. For example, you can use a KProperty to get a property's name and return its type without reading that property from an object.

Kotlin provides some basic features, such as class literals or callable references, as part of the language and standard library. To access more extensive runtime introspection, import the kotlin-reflect library.

The reflection APIs consist of the following packages:

  • kotlin.reflect is part of the standard library and contains core reflection types and functions.

  • kotlin.reflect.full with extensions for inspecting Kotlin declarations.

  • kotlin.reflect.jvm with JVM-specific extensions that connect Kotlin and Java reflections.

Interoperability with Java reflection

On the JVM, the kotlin-reflect library provides extensions in the kotlin.reflect.jvm package for converting between Kotlin and Java reflection objects. For example, you can find the Java method that implements a Kotlin property getter and the property's backing field:

import kotlin.reflect.jvm.* class User(val name: String) fun main() { println(User::name.javaGetter) // public final java.lang.String User.getName() println(User::name.javaField) // private final java.lang.String User.name }

A Kotlin property doesn't always have a corresponding Java field or getter, so both properties return null when no matching Java reflection object exists.

You can also convert between Kotlin KClass and Java Class objects. To get the Kotlin class that corresponds to a Java class, use the .kotlin extension property:

fun getKClass(o: Any): KClass<Any> = value.javaClass.kotlin

Obtain a runtime class

Most reflection operations begin with a KClass. How you obtain it depends on whether the class is known at compile time:

  • Use ClassName::class to reference a class known at compile time.

  • Use value::class to obtain the actual class of a runtime value, which may be more specific than its declared type.

import kotlin.reflect.KClass class User(val name: String) open class Language class Kotlin : Language() fun main() { // Returns a KClass that represents User. val userClass: KClass<User> = User::class println(userClass.simpleName) // User val language: Language = Kotlin() // Returns a KClass that represents // the concrete class of the value at runtime val runtimeClass: KClass<out Language> = language::class println(runtimeClass.simpleName) // Kotlin }

The Kotlin standard library provides basic tools for inspecting classes. For more extensive runtime introspection, add the kotlin-reflect dependency and use its APIs.

Add the JVM dependency

On the JVM platform, the Kotlin compiler distribution includes the runtime component required for using the reflection features as a separate artifact, kotlin-reflect.jar. This allows applications without reflection features to exclude this artifact and reduce the size of the runtime classpath.

To use reflection in a Gradle or Maven project, add the dependency on kotlin-reflect:

dependencies { implementation(kotlin("reflect")) }
<dependencies> <dependency> <groupId>org.jetbrains.kotlin</groupId> <artifactId>kotlin-reflect</artifactId> <version>${kotlin.version}</version> </dependency> </dependencies>

If you don't use Gradle or Maven, make sure you have kotlin-reflect.jar in the classpath of your project. The command-line compiler adds the library by default. To exclude it from the classpath, use the -no-reflect compiler option.

Inspect types

KClass and KType represent different information. A KClass represents a class without preserving type arguments or nullability. For example, List<String> and List<Int> share the same List::class representation, and String and String? share String::class. Therefore, use the typeOf<T>() function to obtain a KType for the statically known type T, including its type arguments and nullability.

In the following example, the classifier property connects the type to its class or type parameter. The arguments collection contains its type arguments. Therefore, the code can inspect String? separately from List:

import kotlin.reflect.typeOf fun main() { val type = typeOf<List<String?>>() println(type.classifier) // class kotlin.collections.List println(type.arguments.single().type) // kotlin.String? }

Check values

The is, as, and as? operators work when you write the target type directly in the code. However, if you store the target class in a KClass, use:

  • The isInstance() function, to check if a value is an instance of the class. It returns true or false.

  • The cast() function, to return the value with the target type. It throws an exception if the value is null or has an incompatible type.

  • The safeCast() function, to return the value with the target type, or null if the value is null or has an incompatible type.

import kotlin.reflect.cast import kotlin.reflect.safeCast fun main() { val expectedClass = String::class val value: Any = "Kotlin" // Checks the value without casting it println(expectedClass.isInstance(value)) // true // Return the value as a String val text = expectedClass.cast(value) println(text.length) // 6 // An incompatible cast throws an exception val failedCast = runCatching { Int::class.cast(value) } println(failedCast.isFailure) // true // A safe cast represents the same mismatch with null val number = Int::class.safeCast(value) println(number) // null }

Inspect a class

After obtaining a KClass, you can inspect its members. This way, you learn which declarations exist before using them. Use the members property to inspect functions and properties of a class. It contains a collection of KCallable objects with all declarations accessible in the class, including inherited declarations.

For example, the following code lists the functions and properties accessible in a data class:

data class User(val name: String, val age: Int) fun main() { println(User::class.members.map { it.name }.sorted()) // [age, component1, component2, copy, equals, hashCode, name, toString] }

The kotlin.reflect.full package provides more specific properties for selecting declarations by kind, scope, and receiver:

Property

Returns

declaredMembers

Functions and properties declared directly in the class, excluding inherited declarations

functions

All functions available from the class: non-static functions from the class and its superclasses, and static functions declared in the class

declaredFunctions

All functions declared in the class

memberFunctions

Non-extension, non-static functions declared in the class and its superclasses

declaredMemberFunctions

Non-extension, non-static functions declared in the class

memberExtensionFunctions

Extension functions declared as members of the class or its superclasses

declaredMemberExtensionFunctions

Extension functions declared in the class

staticFunctions

Static functions declared in the class

memberProperties

Non-extension properties declared in the class and its superclasses

declaredMemberProperties

Non-extension properties declared in the class

memberExtensionProperties

Extension properties declared as members of the class or its superclasses

declaredMemberExtensionProperties

Extension properties declared as members in the class.

staticProperties

Properties representing static fields declared in Java classes

Choose the narrowest property that matches the declarations your code needs. For example, a serializer might use declaredMemberProperties to process only properties introduced by a specific class, while a framework that searches for a callable API might need inherited memberFunctions as well.

You can also inspect sealed subclasses with reflection.

Read a property

Reflection allows you to read a property selected at runtime. For example, you have a UI configuration that specifies name as the property to display from a User object. For that, the application must find the matching property declaration and then invoke its getter:

import kotlin.reflect.full.memberProperties data class User(val name: String, val age: Int) fun readProperty(instance: Any, propertyName: String): Any? { // Inspect the runtime class val runtimeClass = instance::class // Find the property declaration val property = runtimeClass.memberProperties .firstOrNull { it.name == propertyName } ?: error("Unknown property: $propertyName") // Call the getter with the object as a receiver return property.getter.call(instance) } fun main() { val user = User("Jane Doe", 22) println(readProperty(user, "name")) // Jane Doe println(readProperty(user, "age")) // 22 }

This example uses memberProperties to return unbound property objects. These objects describe properties that belong to the class but aren't attached to a particular instance. Therefore, getter.call() needs instance as its receiver. The result has the Any? type because a property selected at runtime can return any type.

Call a function

Dynamic function calls follow the same pattern as properties. If all arguments are available in their declared order, you can use call(). Declare an unbound member function with its object instance first, then add its regular arguments:

import kotlin.reflect.full.memberFunctions class Formatter { fun format(text: String, uppercase: Boolean): String = if (uppercase) text.uppercase() else text } fun main() { val formatter = Formatter() // Find the function by its name at runtime val function = Formatter::class.memberFunctions .single { it.name == "format" } // Supply the receiver first, then the declared arguments in order val result = function.call(formatter, "Kotlin", true) println(result) // KOTLIN }

In this example, the call contains three values: the Formatter receiver, text, and uppercase. It also returns the Any? type and reports an incompatible receiver or argument at runtime.

You can also associate values with KParameter objects instead of supplying them by position. For that, use the callBy(). function:

import kotlin.reflect.full.instanceParameter import kotlin.reflect.full.memberFunctions import kotlin.reflect.full.valueParameters class Greeter { fun greet(name: String, punctuation: String = "!") = "Hello, $name$punctuation" } fun main() { val greeter = Greeter() val function = Greeter::class.memberFunctions .single { it.name == "greet" } // valueParameters contains parameters declared in greet() // but not the Greeter receiver val nameParameter = function.valueParameters .single { it.name == "name" } val result = function.callBy( mapOf( // Member functions need an instance receiver function.instanceParameter!! to greeter, nameParameter to "Kotlin" ) ) println(result) // Hello, Kotlin! }

In this example, instanceParameter identifies the member-function receiver, and valueParameters contains only parameters declared in the function signature.

12 October 2026