kotlin more language structures - > reflection

Posted by Stopofeger on Thu, 27 Jan 2022 23:18:13 +0100

Class reference

The most basic reflection function is to get the runtime reference of Kotlin class. To get a reference to a statically known Kotlin class, you can use class literal syntax

val c = MyClass::class

Note that Kotlin class references are different from java class references. To get a java class reference, use on the KClass instance java properties

 

Bound class reference (since 1.1)

By using the object as the receiver, you can get the reference of the class of the specified object with the same:: class syntax

 val widget: Widget = ......
assert(widget is GoodWidget) { "Bad widget: ${widget::class.qualifiedName}" }

You can get a reference to the exact class of the object, such as GoodWidget or BadWidget, although the type of receiver expression is Widget

 

Callable reference

In addition to being a introspective program structure, function attributes and constructor references can also be used for calling or as instances of function types.

The public supertype of all callable references is kcallable < out R >, where R is the return value type, attribute type for attribute and constructed type for constructor

 

Function reference

When we have a named function declared as follows

fun isOdd(x: Int) = x % 2 != 0

We can easily call it directly (isOdd(5)), but we can also pass it as a value of a function type, for example, to another function. To do this, we use the:: operator

val numbers = listOf(1, 2, 3) 
println(numbers.filter(::isOdd))

Function reference belongs to one of the subtypes of kfunction < out R > and depends on the number of parameters. For example, kfunction3 < T1, T2, T3, R >.

When the type expected by the function is known in the context,:: can be used to overload the function. for example

fun isOdd(x: Int) = x % 2 != 0
fun isOdd(s: String) = s == "brillig" || s == "slithy" || s == "tove"

val numbers = listOf(1, 2, 3) 
println(numbers.filter(::isOdd)) // Reference to isOdd(x: Int)

Alternatively, you can provide the necessary context by storing the method reference in a variable with an explicitly specified type

val predicate: (String) -> Boolean = ::isOdd // Reference to isOdd(x: String)

If we need to use the member function or extension function of the class, it needs to be qualified, such as String::toCharArray.

Note that even if a variable is initialized with a reference to an extended function, the inferred function type will have no receiver (it will have an additional parameter that accepts the receiver object). If you need to change to the function type with receiver, please specify its type explicitly

val isEmptyStringList: List<String>.() -> Boolean = List<String>::isEmpty

  

Example: function combination

Consider the following functions

fun <A, B, C> compose(f: (B) -> C, g: (A) -> B): (A) -> C {
    return { x -> f(g(x)) }
}

It returns a combination of two functions passed to it: compose(f, g) = f(g(*)). Now you can apply it to callable references

fun length(s: String) = s.length

val oddLength = compose(::isOdd, ::length)
val strings = listOf("a", "ab", "abc") 

println(strings.filter(oddLength))

  

Attribute reference

To access attributes as first-class objects in Kotlin, we can also use the:: operator

val x = 1
fun main() {
     println(::x.get())
     println(::x.name)
}

The expression:: x evaluates to an attribute object of type kproperty < int >, which allows us to use get() to read its value, or use the name attribute to get the attribute name

 

For variable attributes, such as var y = 1,:: y returns a value of kmutableproperty < int > type, which has a set() method

var y = 1

fun main() {
     ::y.set(2)
     println(y)
}

Property references can be used where a function with a single generic parameter is expected

val strs = listOf("a", "bc", "def") 
println(strs.map(String::length))

To access a property belonging to a member of a class, we qualify it this way

class A(val p: Int)
val prop = A::p 
println(prop.get(A(1)))

For extended properties

val String.lastChar: Char 
    get() = this[length - 1]

fun main() { 
    println(String::lastChar.get("abc"))
}

  

Interoperability with Java reflection

On the JVM platform, the standard library contains an extension of the reflection class, which provides mapping with java reflection objects (refer to the kotlin.reflect.jvm package). For example, to find a behind the scenes field or Java method used as a getter for the Kotlin attribute, write this

  

import kotlin.reflect.jvm.*

class A(val p: Int)

fun main() {
    println(A::p.javaGetter) // Output "public final int A.getP()" 
     println(A::p.javaField) // Output "private final int A.p"
}

To get the kotlin class corresponding to the Java class, use Kotlin extended properties

fun getKClass(o: Any): KClass<Any> = o.javaClass.kotlin

  

Constructor reference

Constructors can be referenced like methods and properties. They can be used anywhere a function type object is expected: it takes the same parameters as the constructor and returns an object of the corresponding type. Reference the constructor by using the:: operator and adding the class name. Consider the following function, which expects a function parameter that has no parameters and returns Foo type

class Foo

fun function(factory: () -> Foo) {
     val x: Foo = factory()
}

Using:: Foo, the zero parameter constructor of class Foo, we can simply call it

function(::Foo)

The callable reference type of the constructor is also one of the subtypes of kfunction < out R >, depending on the number of its parameters

 

Bound function and property references (since 1.1)

You can reference instance methods of specific objects

val numberRegex = "\\d+".toRegex()
println(numberRegex.matches("29"))

val isNumber = numberRegex::matches 
println(isNumber("29"))

Instead of calling method matches directly, we store its references. Such a reference is bound to its recipient. It can be called directly (as shown in the above example) or used whenever a function type expression is expected

val numberRegex = "\\d+".toRegex()
val strings = listOf("abc", "124", "a70") println(strings.filter(numberRegex::matches))

Compare the bound type with the reference of the corresponding unbound type. A bound callable reference has its receiver "attached" to it, so the receiver's type is no longer a parameter

val isNumber: (CharSequence) -> Boolean = numberRegex::matches 
val matches: (Regex, CharSequence) -> Boolean = Regex::matches

Property references can also be bound

val prop = "abc"::length 
println(prop.get())

Since Kotlin 1.2, there is no need to explicitly specify this as the receiver: this::foo is equivalent to:: foo

 

Bound constructor reference

The callable reference of the binding of the constructor of the inner class can be obtained by providing an instance of the external class

class Outer {
    inner class Inner
}

val o = Outer()
val boundInnerCtor = o::Inner