Fading Coder

One Final Commit for the Last Sprint

Home > Tech > Content

Kotlin Reflection: Complete Guide to Runtime Class and Function References

Tech 2

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

Related Articles

Understanding Strong and Weak References in Java

Strong References Strong reference are the most prevalent type of object referencing in Java. When an object has a strong reference pointing to it, the garbage collector will not reclaim its memory. F...

Comprehensive Guide to SSTI Explained with Payload Bypass Techniques

Introduction Server-Side Template Injection (SSTI) is a vulnerability in web applications where user input is improper handled within the template engine and executed on the server. This exploit can r...

Implement Image Upload Functionality for Django Integrated TinyMCE Editor

Django’s Admin panel is highly user-friendly, and pairing it with TinyMCE, an effective rich text editor, simplifies content management significantly. Combining the two is particular useful for bloggi...

Leave a Comment

Anonymous

◎Feel free to join the discussion and share your thoughts.