Reflection is a set of language and library features that allows for introspecting the structure of your own program at runtime. Kotlin makes functions and properties first-class citizens in the language, and introspecting them (i.e. learning a name or a type of a property or function at runtime) is closely intertwined with simply using a functional or reactive style.
The most basic reflection feature is getting the runtime reference to a Kotlin class. To obtain the reference to a statically known Kotlin class, you can use the class literal syntax:
The reference is value of type KClass.
Note that a Kotlin class reference is not the same as a Java class reference. To obtain a Java class reference, use the .java
property on a KClass
instance.
You can get the reference to a class of a specific object with the same ::class
syntax by using the object as a receiver:
SomeClass.class; // The .class Syntax
someInstance.getClass(); // Object.getClass()
References to functions, properties, and constructors, apart from introspecting the program structure, can also be called or used as instances of function types.
The common sypertype for all callabel references is KCallable<out R>, where R
is
When we have a named function declared like this:
We can easily call it directly (isOdd(5)
), but we can also use it as a function type value, e.g. pass it to another function. To do this, we use the ::
operator :
Here ::isOdd
is a value of function type (Int) -> Boolean
.
Function references belong to one of the KFunction<out R> subtypes, depending on the parameter count, e.g. KFunction3<T1, T2, T3, R>
.
::
can be used with overloaded functions when the expected type is known from the context.
For example :
Alternatively, you can provide the necessary context by storing the method reference in a variable with an explicitly specified type:
If we need to use a member of a class, or an extension function, it needs to be qualified, e.g. String::toCharArray
.
Note that even if you initialize a variable with a reference to an extension function, the inferred function type will have no receiver (it will have an additional parameter accepting a receiver object). To have a function type with receiver instead, specify the type explicitly:
Receivers
The signature of a member function or an extension function begins with a receiver : the type upon which the function can be invoked. For example, the signature of
toString()
isAny.() -> String
- it can be called on any non-null object(the receiver), it taks no parameters, and it returns aString
. it is possible to write a lambda function with such a signature - this is called a function literal with receiver, and is extremely useful for building DSLs.A function literal with receiver is perhaps easiest to think of as an extension function in the form of a lambda expression. The declaration looks like an ordinary lambda expression; what makes it take a receiver is the context - it must be passed to a function that takes a function with receiver as a parameter, or assigned to a variable/property whose type is a function type with receiver. The only way to use a function with receiver is to invoke it on an instance of the receiver class, as if it were a member function or extension function. For example:
class Car(val horsepowers: Int) val boast: Car.() -> String = { I'm a car with $horsepowers HP!" } val car = Car(120) println(car.boast())
Inside a lambda expression with receiver, you can use
this
to refer to the receiver object (in this case,car
). As usual, you can omitthis
if there are no naming conflicts, which is why we can simply say$horsepowers
instead of${this.horsepowers}
. So beware that in Kotlin,this
can have different meanings depending on the context: if used inside (possibly nested) lambda expressions with receivers, it refers to the receiver object of the innermost enclosing lambda expression with receiver. If you need to "break out" of the function literal and get the "original"this
(the instance the member function you're inside is executing on), mention the containing class name afterthis@
- so if you're inside a function literal with receiver inside a member function or Car, usethis@Car
.As with other function literals, if the function takes one parameter (other than receiver object that it is invoked on), the single parameter is implicitly called
it
, unless you declare another name. If it takes more than one parameter, you must declare their names.Here's a small DSL example for constructing tree structures:The block after
tree("weightlifting")
is the first function literal with receiver, which will be passed totree()
as theinitialize
parameter. According to the parameter list oftree()
, the receiver is of typeTreeNode
, and therefor,tree()
can callinitialize()
onroot
.root
then becomesthis
insided the scope of that lambda expression, so when we callnode("upper-body")
, it implicitly saysthis.node("upper-body")
, wherethis
refers to the sameTreeNode
asroot
. The next block is passed toTreeNode.node()
, and is invoked on the first child of theroot
node, namelyupper-body
, and inside it,this
will refer toupper-body
.
Consider the following function:
It reterns a composition of two functions passed to it: compose(f,g) = f(g(*))
. Now, you can apply it to callable references:
To access properties as first-class objects in Kotlin, we can also use the ::
operator:
The expression ::x
evaluates to a property object of type KProperty<Int>
, which allows us to read its value using get()
or retrieve the property name using the name
property. For more information, please refer to the docs on the KProperty class.
For a mutable property, e.g. var y = 1
, ::y
returns a value of type KMutableProperty<Int>, which has a set()
method:
var y = 1
fun main() {
::y.set(2)
println(y)
}
A property reference can be used where a function with a single generic parameter is expected:
To access a property that is a member of a class, we qualify it:class A(val p: Int)
val prop = A::p
println(prop.get(A(1)))
For an extension property:
val String.lastChar: Char
get() = this[length - 1]
fun main() {
println(String::lastChar.get("abc"))
}
On the JVM platform, standard library contains extensions for reflection classes that provide a mapping to and from Java reflection objects (see package kotlin.reflect.jvm
). For example, to find a backing field or a Java method that serves as a getter for a Kotlin property, you can say something like this:
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"
}
To get the Kotlin class corresponding to a Java class, use the .kotlin
extension property:
fun getKClass(o: Any): KClass<Any> = o.javaClass.kotlin
Constructors can be referenced just like methods and properties. They can be used wherever an object of function type is expected that takes the same parameters as the constructor and returns an object of the appropriate type. Constructors are referenced by using the ::
operator and adding the class name. Consider the following function that expects a function parameter with no parameters and return type Foo
:
class Foo
fun function(factory: () -> Foo) {
val x: Foo = factory()
}
Using ::Foo
, the zero-argument constructor of the class Foo, we can simply call it like this:
function(::Foo)
Callable references to constructors are typed as one of the KFunction<out R> subtypes, denpedning on the parameter count.
You can refer to an instance method of a particular object:
Instead of calling the method matches
directly we are storing a reference to it. Such reference is bound to its receiver. it can be called directly (like in the example above) or used whenever an expression of function type is expected:
A bound callable reference to a constructor of an inner class can be obtained by providing an instance of the outer class: