Kotlin Reflection: Complete Guide to Runtime Class and Function References
Class References
The fundamental reflection capability involves obtaining runtime references to Kotlin classes. To retrieve a reference to a statically known Kotlin class, use the class literal syntax:
val customClass = MyClass::class
Kotlin class references differ from Java class references. To obtain a Java class reference, access the .java property on a KClass instance.
Bound Class References (Since Kotlin 1.1)
You can obtain a class reference for a specific object by using the object as the receiver with the same ::class syntax:
val widget: Widget = getWidget()
assert(widget is ValidWidget) { "Invalid widget: ${widget::class.qualifiedName}" }
This approach retrieves the precise class reference (such as ValidWidget or InvalidWidget), even when the receiver expression's compile-time type is Widget.
Callable References
References to functions, properties, and constructors can serve not only for introspection but also for invocation or as instances of function types.
The common supertype for all callable references is KCallable<out R>, where R represents the return type (property type for properties, constructed type for constructors).
Function References
Consider a named function declared as follows:
fun isEven(value: Int) = value % 2 == 0
While you can directly invoke it with isEven(5), you can also treat it as a function-type value by passing it to another function. Use the :: operator for this purpose:
val numbers = listOf(1, 2, 3, 4, 5)
println(numbers.filter(::isEven))
Functon references are subtypes of KFunction<out R>, specifically KFunction1, KFunction2, and so on, depending on the parameter count.
Overloaded Functions
When the expected function type is known from context, :: can resolve overloaded functions:
fun isEven(value: Int) = value % 2 == 0
fun isEven(text: String) = text == "even" || text == "odd" || text == "prime"
val numbers = listOf(1, 2, 3, 4, 5)
println(numbers.filter(::isEven)) // Resolves to isEven(x: Int)
Alternatively, provide context by storing the method reference in a variable with an explicitly specified type:
val predicate: (String) -> Boolean = ::isEven // Resolves to isEven(x: String)
For class member functions or extension functions, qualify the reference appropriately, such as String::toByteArray.
Extension Function Type Details
Even when initializing a variable with an extension function reference, the inferred function type lacks a receiver—it includes an additional parameter for the receiver object. To create a function type with a receiver, specify the type explicitly:
val isBlankStringList: List<String>.() -> Boolean = List<String>::isEmpty
Example: Function Composition
Consider this higher-order function:
fun <P, Q, R> compose( transformer: (Q) -> R,
converter: (P) -> Q): (P) -> R {
return { input -> transformer(converter(input)) }
}
This returns a composition of two passed functions: compose(f, g) = f(g(*)). Apply it to callable references:
fun computeLength(text: String) = text.length
val hasOddLength = compose(::isEven, ::computeLength)
val words = listOf("a", "ab", "abc", "abcd")
println(words.filter(hasOddLength))
Property References
Access properties as first-class objects in Kotlin using the :: operator:
val counter = 100
fun main() {
println(::counter.get())
println(::counter.name)
}
The expression ::counter evaluates to a property object of type KProperty<Int>, enabling value retrieval via get() or property name access via name.
For mutable properties defined with var:
var multiplier = 1
fun main() {
::multiplier.set(5)
println(multiplier)
}
Property references work in generic function contexts:
val words = listOf("hello", "world", "kotlin")
println(words.map(String::length))
Access class member properties with qualification:
class Container(val item: Int)
val propertyRef = Container::item
println(propertyRef.get(Container(42)))
For extension properties:
val String.firstCharacter: Char
get() = this[0]
fun main() {
println(String::firstCharacter.get("Kotlin"))
}
Java Reflection Interoperability
On the JVM platform, the standard library includes reflection extensions providing mappings between Kotlin and Java reflection objects (see kotlin.reflect.jvm package). To find the backing field or Java method used as a Kotlin property getter:
import kotlin.reflect.jvm.*
class Container(val item: Int)
fun main() {
println(Container::item.javaGetter) // Output: "public final int Container.getItem()"
println(Container::item.javaField) // Output: "private final int Container.item"
}
Obtain the Kotlin class corresponding to a Java class using the .kotlin extension property:
fun getKotlinClass(obj: Any): KClass<Any> = obj.javaClass.kotlin
Constructor References
Reference constructors like methods and properties. Use them wherever a function-type object is expected—matching the constructor's parameters and returning the constructed type. Reference constructors using :: with the class name:
class Product
fun instantiate(factory: () -> Product) {
val result: Product = factory()
}
Using ::Product (the zero-argument constructor), invoke it simply:
instantiate(::Product)
Constructor callable reference types are also subtypes of KFunction<out R>, determined by parameter count.
Bound Function and Property References (Since Kotlin 1.1)
Reference instance methods of specific objects:
val digitPattern = "\\d+".toRegex()
println(digitPattern.matches("99"))
val checkDigits = digitPattern::matches
println(checkDigits("99"))
Instead of calling matches directly, store its reference. Such references bind to their receivers and can be invoked directly or used wherever a function-type expression is expected:
val digitPattern = "\\d+".toRegex()
val samples = listOf("abc", "123", "b456")
println(samples.filter(digitPattern::matches))
Compare bound references with corresponding unbound types. Bound callable references have their receiver "attached," so the receiver type is no longer a paramter:
val checkDigits: (CharSequence) -> Boolean = digitPattern::matches
val matches: (Regex, CharSequence) -> Boolean = Regex::matches
Property references can also be bound:
val lengthProperty = "example"::length
println(lengthProperty.get())
Since Kotlin 1.2, explicit this as receiver is unnecessary: this::foo is equivalent to ::foo.
Bound Constructor References
Bound callable references to inner class constructors can be obtained by providing an instance of the outer class:
class OuterShell {
inner class InnerShell
}
val shell = OuterShell()
val boundInnerReference = shell::InnerShell