Kotlin get field by name

Как получить ссылку на свойство из класса данных по наименованию этого свойства?

Вложенные классы H, K и т.д., также являются классами данных и могут содержать в себе либо другие вложенные классы данных, либо базовые классы kotlin. Так же имеется ИЗМЕНЯЕМЫЙ список строк, содержащий в себе перечень свойств класса A и вложенных в него классов — путь к конкретному свойству — вида:

val breadCrumb : MutableList = mutableListOf("h", "k", . "z") 

Необходимо пройти по этому пути и установить значение свойства некоторого вложенного класса данных по аналогии c:

val a = A(. ) a.h.k. z = "new value" 
var curItem: Any = A(. ) for (itemName in breadCrumb) < . for(dataField in curItem::class.java.fields) < if(dataField.get(curItem).name == itemName)< when(dataField.type.simpleName)< "String" ->dataField .set(curItem, value.text) "Integer" -> dataField .setInt(curItem, value.text as Int) "Float" -> dataField .setFloat(curItem, value.text as Float) "Boolean" -> dataField .setBoolean(curItem, value.text as Boolean) > . curItem = dataField.java.get(curItem) > > > 

После обсуждения

Решения на основе рефлексии в Котлине так и не найдено. По рекомендации коллег пришлось отказаться от затеи сохранять данные в кастомном классе данных. Попробую решить с использованием класса JsonObject:

fun setValue(field: JSONUIField, value: JSONUIValue) < var curItem: Any = A.getAsJsonObject("a") for (itemName in breadCrumb) < if ((curItem as JsonObject).getAsJsonObject(itemName) == null) curItem.add(itemName, JsonObject()) curItem = curItem.getAsJsonObject(itemName) >when (field.type) < "String" ->(curItem as JsonObject).addProperty(field.name, value.value) "Integer" -> (curItem as JsonObject).addProperty(field.name, (value.value). toInt()) "Float" -> (curItem as JsonObject).addProperty(field.name, (value.value). toFloat()) "Boolean" -> (curItem as JsonObject).addProperty(field.name, (value.value). toBoolean()) > > 

Источник

Читайте также:  Python site packages example

Reflection

Reflection is a set of language and library features that allows you to introspect the structure of your program at runtime. Functions and properties are first-class citizens in Kotlin, and the ability to introspect them (for example, learning the name or the type of a property or function at runtime) is essential when using a functional or reactive style.

Kotlin/JS provides limited support for reflection features. Learn more about reflection in Kotlin/JS.

JVM dependency

On the JVM platform, the Kotlin compiler distribution includes the runtime component required for using the reflection features as a separate artifact, kotlin-reflect.jar . This is done to reduce the required size of the runtime library for applications that do not use reflection features.

To use reflection in a Gradle or Maven project, add the dependency on kotlin-reflect :

If you don’t use Gradle or Maven, make sure you have kotlin-reflect.jar in the classpath of your project. In other supported cases (IntelliJ IDEA projects that use the command-line compiler or Ant), it is added by default. In the command-line compiler and Ant, you can use the -no-reflect compiler option to exclude kotlin-reflect.jar from the classpath.

Class references

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 a KClass type value.

On JVM: 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.

Bound class references

You can get the reference to the class of a specific object with the same ::class syntax by using the object as a receiver:

You will obtain the reference to the exact class of an object, for example, GoodWidget or BadWidget , regardless of the type of the receiver expression ( Widget ).

Callable references

References to functions, properties, and constructors can also be called or used as instances of function types.

Function references

When you have a named function declared as below, you can call it directly ( isOdd(5) ):

Alternatively, you can use the function as a function type value, that is, pass it to another function. To do so, use the :: operator:

Here ::isOdd is a value of function type (Int) -> Boolean .

:: 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 you need to use a member of a class or an extension function, it needs to be qualified: String::toCharArray .

Even if you initialize a variable with a reference to an extension function, the inferred function type will have no receiver, but it will have an additional parameter accepting a receiver object. To have a function type with a receiver instead, specify the type explicitly:

Example: function composition

Consider the following function:

It returns a composition of two functions passed to it: compose(f, g) = f(g(*)) . You can apply this function to callable references:

Property references

To access properties as first-class objects in Kotlin, use the :: operator:

The expression ::x evaluates to a KProperty0 type property object. You can read its value using get() or retrieve the property name using the name property. For more information, see the docs on the KProperty class.

For a mutable property such as var y = 1 , ::y returns a value with the KMutableProperty0 type which has a set() method:

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, qualify it as follows:

For an extension property:

Interoperability with Java reflection

On the JVM platform, the 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 write something like this:

To get the Kotlin class that corresponds to a Java class, use the .kotlin extension property:

Constructor references

Constructors can be referenced just like methods and properties. You can use them wherever the program expects a function type object 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 :

Using ::Foo , the zero-argument constructor of the class Foo , you can call it like this:

Callable references to constructors are typed as one of the KFunction subtypes depending on the parameter count.

Bound function and property references

You can refer to an instance method of a particular object:

Instead of calling the method matches directly, the example uses a reference to it. Such a reference is bound to its receiver. It can be called directly (like in the example above) or used whenever a function type expression is expected:

Compare the types of the bound and the unbound references. The bound callable reference has its receiver «attached» to it, so the type of the receiver is no longer a parameter:

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

A property reference can be bound as well:

You don’t need to specify this as the receiver: this::foo and ::foo are equivalent.

Bound constructor references

A bound callable reference to a constructor of an inner class can be obtained by providing an instance of the outer class:

Источник

Accessing an objects value by fieldName in Kotlin

For this tho I need to use class.java.getDeclaredField(«bar») and other Java things like trySetAccessible() and get . Is it possible to do this in a more Kotlin way?

2 Answers 2

Kotlin reflection doesn’t have functions like getDeclaredField() . Instead, it has functions returning collections, like getMemberProperties() , and you can use the Iterable extension functions to find what you need. Pass an instance of the class as the first parameter of the getter/setter you want to use.

 val value = Foo::class.memberProperties .first < it.name == "bar" >.also < it.isAccessible = true >// if it's not visible .getter(foo) 

Note to do this without casting you need to use Foo::class instead of foo::class because the latter is a Class so the getters it returns are not usable due to variance.

But if you’re using reflection, you may not know the class you’re working with, in which case you’d have to cast the returned property before using it.

 val value = foo::class.memberProperties .first < it.name == "bar" >.also < it.isAccessible = true >.let < it as KProperty1> .getter(foo) // or val value = (foo::class as KClass).memberProperties .first < it.name == "bar" >.also < it.isAccessible = true >.getter(foo) 

is it possible to also edit the value in the field? Like to make it mutable and then edit the value and set it to immutable again?

Kotlin reflection does not have any way to access fields, so it can’t be used to do this. It only knows of properties, and doesn’t even have a way to detect if those properties utilize a backing field or not. So you have to use Java reflection to do that.

You can do it with Kotlin reflection like this:

val prop = (foo::class as KClass).memberProperties.single < it.name == "bar" >println(prop.get(foo)) 

Note that contrary to Java, you can’t use this technique to set the value of read-only field ( val ). The reason is: in Kotlin we don’t really deal with fields, but with properties. And property is a getter and optionally a setter. If there is no setter then there is nothing really to invoke. val may not be backed by a field, it may not hold any value at all. In such case it would not make sense to even consider setting the value of a property.

Of course, Kotlin reflection API could provide optional access to fields if property is backed by one, but right now I believe there is no representation for fields in public API.

Источник

Kotlin data class: how to read the value of property if I don’t know its name at compile time?

How can I read the value of property in a Kotlin data class instance if the property name is only known at runtime?

4 Answers 4

Here is a function to read a property from an instance of a class given the property name (throws exception if property not found, but you can change that behaviour):

import kotlin.reflect.KProperty1 import kotlin.reflect.full.memberProperties @Suppress("UNCHECKED_CAST") fun readInstanceProperty(instance: Any, propertyName: String): R < val property = instance::class.members // don't cast here to , it would succeed silently .first < it.name == propertyName >as KProperty1 // force a invalid cast exception if incorrect type here return property.get(instance) as R > 

build.gradle.kts

Using

// some data class data class MyData(val name: String, val age: Int) val sample = MyData("Fred", 33) // and reading property "name" from an instance. val name: String = readInstanceProperty(sample, "name") // and reading property "age" placing the type on the function call. val age = readInstanceProperty(sample, "age") println(name) // Fred println(age) // 33 

I know this answer is almost 2 years old. is this still the way to do it? I’m curious if there is anything new that may have been added to the Kotlin API that would have created another way to achieve this.

@HatemJaber If you are reading a property where you do not know the name at compile time, then yes. If you want to read a known property that is there at compile time you can use a property reference to access the property.

@HatemJaber: newer versions of the reflection library allow for instance::class.members instead of instance.javaClass.kotlin.declaredMemberProperties, though you should check that the type of the property is a KProperty` or KMutableProperty .

@AdamKis My answer still worked if a correct import statement was added, I have updated it to a newer version and included the imports.

@outis you don’t need to check that because they all descend from KProperty1 as the above example was changed to handle.

You can do it through reflection, and it’s all the same for data classes and normal ones.

The first option is just to use Java reflection:

val name = obj.javaClass .getMethod("getName") // to get property called `name` .invoke(obj) 

You can even make an extension function:

inline fun Any.getThroughReflection(propertyName: String): T? < val getterName = "get" + propertyName.capitalize() return try < javaClass.getMethod(getterName).invoke(this) as? T >catch (e: NoSuchMethodException) < null >> 

It calls the public getter. To get a value of a private property you can modify this code using getDeclaredMethod and setAccessible . This will also work for Java objects with the corresponding getters (but it misses the convention of is and has getters for boolean ).

data class Person(val name: String, val employed: Boolean) val p = Person("Jane", true) val name = p.getThroughReflection("name") val employed = p.getThroughReflection("employed") println("$name - $employed") // Jane - true 

The second option involves using kotlin-reflect library which you should add to your project separately, here’s its documentation. It will let you get actual Kotlin property value, ignoring Java getters.

You can use javaClass.kotlin to get actual Kotlin class token, and then you get a property from it:

val name = p.javaClass.kotlin.memberProperties.first < it.name == "name" >.get(p) 

This solution will work only for Kotlin classes, not for Java ones, but it is much more reliable if you need to work with Kotlin classes: it doesn’t depend on the underlying implementation.

Источник

Оцените статью