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.reflectis part of the standard library and contains core reflection types and functions.kotlin.reflect.fullwith extensions for inspecting Kotlin declarations.kotlin.reflect.jvmwith 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:
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:
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::classto reference a class known at compile time.Use
value::classto obtain the actual class of a runtime value, which may be more specific than its declared type.
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:
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:
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 returnstrueorfalse.The
cast()function, to return the value with the target type. It throws an exception if the value isnullor has an incompatible type.The
safeCast()function, to return the value with the target type, ornullif the value isnullor has an incompatible type.
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:
The kotlin.reflect.full package provides more specific properties for selecting declarations by kind, scope, and receiver:
Property | Returns |
|---|---|
Functions and properties declared directly in the class, excluding inherited declarations | |
All functions available from the class: non-static functions from the class and its superclasses, and static functions declared in the class | |
All functions declared in the class | |
Non-extension, non-static functions declared in the class and its superclasses | |
Non-extension, non-static functions declared in the class | |
Extension functions declared as members of the class or its superclasses | |
Extension functions declared in the class | |
Static functions declared in the class | |
Non-extension properties declared in the class and its superclasses | |
Non-extension properties declared in the class | |
Extension properties declared as members of the class or its superclasses | |
Extension properties declared as members in the class. | |
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:
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:
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:
In this example, instanceParameter identifies the member-function receiver, and valueParameters contains only parameters declared in the function signature.