Photograph via Yeshi Kangrang on Unsplash

Reflection is an impressive framework that provides us the gear to check out the construction of each categories and items to be had at runtime. Like Java and plenty of different languages that run at the JVM, Kotlin helps mirrored image. However… is it very other from Java?

APIs to be had

Kotlin makes use of 2 other mirrored image libraries:

  • the good-old-buddy from the Java international “java.lang.reflection” bundle.
  • the new-and-shiny “kotlin-reflect” bundle, to be had here. In contrast to the Java bundle, this one must be added explicitly to our undertaking if we wish to use kotlin mirrored image.

Having those 2 elements on the similar time implies that Kotlin provides 100% compatibility with the Java mirrored image mechanism, but in addition supplies a brand new library in order to regulate some particular options, such because the nullability gadget, for example.

The Kotlin replicate API

It comprises a hierarchy of categories and interfaces that constitute each and every one of the most elements declared in our supply code: categories, subclasses, constructors, strategies, homes, serve as parameters, annotations and so forth.

The primary elements are proven in the next symbol:

Kotlin replicate API

A majority of these categories use generics, in order that they obtain one or more than one kind arguments. For example:

//XXX: object representing a category, extra exactly the "String" elegance
val clazz1 : KClass<String> = ...

//XXX: every other object representing every other elegance, in this example the "Number" elegance 
val clazz2 : KClass<Quantity> = ...

When operating with mirrored image, the supply code would possibly appear extra difficult, however stay in thoughts that the foundations of O.O.P. nonetheless observe. Beneath the hood, we will be able to be simply growing circumstances of categories and the usage of a few of its homes or the way to “inspect” our code.

The next sections will discover the API in extra element. On nearly each and every instance we will be able to observe mirrored image over the next customized knowledge elegance:

knowledge elegance Particular person(val personName : String, val personAge : Int)

KClass<>

Cases of KClass constitute a kotlin elegance. Its kind parameter defines the particular elegance represented (String, Quantity, Particular person, or some other). Every one of the most categories used as kind arguments have the similar parts (constructors, strategies, attributes, and so forth) however clearly their contents range.

KClass circumstances may also be received:

  • the usage of an object, in the course of the javaClass.kotlin assets.
  • the usage of a selected elegance title, gaining access to its ::elegance characteristic.
val p = Particular person("John", 20)

//XXX: get right of entry to the usage of object
val pClazz1 : KClass<Particular person> = p.javaClass.kotlin

//XXX: get right of entry to the usage of elegance
val pClazz2 : KClass<Particular person> = Particular person::elegance

println(pClazz1.title)
println(pClazz2.title)

Lots of the occasions, KClass acts as an access level to the mirrored image international, as a result of thru it we will retrieve:

  • the inspected elegance title
  • its constructors
  • its homes
  • its strategies
  • its subclasses
  • and so forth…

The next snippet, for example, retrieves the constructors of our Particular person elegance:

val clazz : KClass<Particular person> = Particular person::elegance
val construcs = clazz.constructors
    
construcs.forEach {    
    println(it)    
}

KCallable<>

The KCallable interface represents a means or an characteristic of a category. Its kind parameter specifies:

  • the kind of a given assets (when carried out on attributes)
  • a serve as go back kind (when carried out over strategies)

KCallable broadcasts the name() means, permitting us to invoke a serve as or an accessor (when carried out on attributes).

val p = Particular person("John", 20)

//XXX: ref to age (Int) assets 
var pCall : KCallable<Int> = p::personAge
    
//XXX: name() invokes accessor
println("${p.personName}'s age is ${pCall.call()}")

On the other hand, stay in thoughts that name() is now not a kind secure means. It may be invoked with any choice of parameters and kinds, however we will be able to get runtime mistakes if the anticipated values and the won ones don’t fit.

KFunction<>

KFunction inherits from KCallable and is every other abstraction used to constitute handiest strategies. Its kind parameter specifies the knowledge kind returned via the serve as invocation.

val p = Particular person("John", 20)

val mFun : KFunction<Boolean> = p::isAdult

val outcome = mFun.name(21)

println("Result is $result of type ${result.javaClass}")

Elegance constructors are purposes too, in order that they’re additionally represented the usage of this interface.

Amongst different homes, KFunction comprises a number of at hand flags that fit each and every one of the most serve as modifiers to be had when stating a serve as:

  • isSuspend (droop modifier)
  • isInline (inline modifier)
  • isOpen (open modifier)

KProperty<>

KProperty additionally inherits from KCallable, but it surely represents an characteristic as an alternative of a serve as. We will be able to additionally use it to check out native/world variables and constants.

In many of the instances, when making use of mirrored image on those parts, we will be able to use KProperty subclasses as an alternative of the superclass itself. To simplify issues, let’s imagine that this hierarchy comprises subclasses relying on:

  • a assets mutability: is the component inspected declared as mutable (var) or immutable (val)?
  • a assets scope: is the component a category assets, an area variable or an international one…? (NOTE: this isn’t 100% correct, as a result of in truth the library exams for class receivers as an alternative of scopes).

So, making an allowance for each facets, we’ve got to be had subclasses equivalent to:

  • KProperty0<>, carried out on constants.
  • KMutableProperty0<>, carried out on variables.
  • KProperty1<>, used with immutable elegance attributes.
  • KMutableProperty1<>, used with read-write elegance homes.

Ok(Mutable)Property0<T> takes a unmarried argument (T) representing the kind of the variable/consistent inspected and broadcasts the corresponding get()/set() strategies:

//XXX: some world consistent of kind Int...
const val THRESHOLD = 18

//XXX: ... may also be inspected with
val mGlobalProp : KProperty0<Int> = ::THRESHOLD

println("global var has value ${mGlobalProp.get()}")

However, Ok(Mutable)Property1<C, P> takes 2 kind arguments, one for the kind elegance containing the valuables inspected (C) and every other one for the valuables itself (P):

val p1 = Particular person("John", 40)
    
val mProp : KProperty1<Particular person, Int> = Particular person::personAge
    
println("${p1.personName}'s age is ${mProp.get(p1)}")

The valuables hierarchy additionally comprises the KProperty2<> interface, to be had for extension attributes.

KAnnotatedElement

This interface acts because the superclass for all parts in the kotlin-reflect API, as a result of annotations may also be carried out on any part (a category, a serve as, a assets, even a variable may also be annotated in Kotlin).

KAnnotatedElement isn’t a generic interface: it handiest defines a assets referred to as “annotations” consisting on a listing of annotations to be had at runtime. This assets is inherited via all subclasses, so each and every example of KClass, KProperty, KFunction… has its corresponding annotation record.

As a way to simply traverse those parts, the category additionally broadcasts the “findAnnotation()” means, that appears for some annotation of a given kind and returns it when it’s provide (or null if it isn’t):

val mProp : KProperty1<> = ...
//XXX: be expecting conceivable null values motive annotation might not be declared
val mCustomAnnotation : MyCustomAnnotation? = mProp.findAnnotation<MyCustomAnnotation>()
     
mCustomAnnotation?.let {
   println(it.annotationClass )
   println(it.worth)
}

Wrapping up

Kotlin is absolutely suitable with Java mirrored image and offers us the facility to dinamically manipulate unkown parts: categories, purposes, strategies, annotations… they all have their corresponding component in the kotlin-reflect API and may also be accessed at runtime.

As same old, test this sample code to present mirrored image a take a look at.

Write you subsequent time!

LEAVE A REPLY

Please enter your comment!
Please enter your name here