How can I get the name of a Kotlin property? - reflection

I have the following function to access a property's delegate. It uses Kotlin reflection to get a property's name and Java reflection to get the field.
fun Any.getDelegate<T>(prop: KProperty<T>): Any {
return javaClass.getDeclaredField("${prop.name}\$delegate").let {
it.setAccessible(true)
it.get(this)
}
}
The method is used like this:
val delegate = a.getDelegate(A::b)
However, I would prefer to use it like this:
val delegate = a.b.delegate
The problem with the code above is getting the property name of a.b and getting the instance a from a.b. From what I know about Kotlin, this is probably not possible, however I'd like to see if I can clean up my function at all.
To give a bigger picture of what I'm trying do here's my complete code. I want an observable delegate to which I can add and remove observers using the delegate reference and without creating addition variables.
fun Any.addObservable<T>(prop: KProperty<T>, observer: (T) -> Unit) {
getObservableProperty(prop).observers.add(observer)
}
fun Any.getObservableProperty<T>(prop: KProperty<T>): ObservableProperty<T> {
return getDelegate(prop) as ObservableProperty<T>
}
fun Any.getDelegate<T>(prop: KProperty<T>): Any {
return javaClass.getDeclaredField("${prop.name}\$delegate").let {
it.setAccessible(true)
it.get(this)
}
}
class ObservableProperty<T>(
initialValue: T,
initialObservers: Array<(T) -> Unit> = emptyArray()) : ReadWriteProperty<Any?, T> {
private var value = initialValue
public val observers: MutableSet<(T) -> Unit> = initialObservers.toHashSet()
public override fun get(thisRef: Any?, desc: PropertyMetadata): T {
return value
}
public override fun set(thisRef: Any?, desc: PropertyMetadata, value: T) {
this.value = value
observers.forEach { it(value) }
}
}
class A() {
var b by ObservableProperty(0)
}
fun main(args: Array<String>) {
val a = A()
a.addObservable(A::b) {
println("b is now $it")
}
a.b = 1
a.b = 2
a.b = 3
}
Edit:
I just realized that the function also isn't strict because the property delegate field name is referenced by KProperty name, which doesn't require a strong reference to the enclosing class. Here's an example to demonstrate the problem:
class A() {
var foo by ObservableProperty(0)
}
class B() {
var foo by ObservableProperty(0)
}
fun main(args: Array<String>) {
val a = A()
a.addObservable(B::foo) {
println("b is now $it")
}
a.foo = 1
a.foo = 2
a.foo = 3
}
This compiles and runs without error because A::foo and B::foo both result in a field string of "foo$delegate.

Right now reflection is all we can do to get to the delegate object. We are designing a language feature to have direct access to delegate instance, but it's long way to go.

This is how you get the name of a Kotlin Property (although only with an instance of the class). This part will be useful to anyone arriving at this question purely based off its title.
class Stuff(val thing: String)
val stuff = Stuff("cool stuff")
val thingFieldName = "${stuff.thing}\$delegate"
// value of thingFieldName is now "thing"
In terms of getting the delegate itself easier, they say you can now do this:
class Foo {
var bar: String by ReactiveProperty<String>()
}
val foo = Foo()
val bar = foo.bar
val barDelegate = ... // foo.bar$delegate
See ticket.

Related

Async Loading of a TreeView

Hey I am very new to tornadofx struggeling with async loading of data for the treeview. I am loading categories from a rest endpoint, which I want to show in there.
It seems like there's no direct data binding to the children.
when using 'bindChildren' I can provide the observable list, but I have to convert them into Node's. which then would make the populate block kind of obsolete.
What's the recommended way of doing this? I cannot find anything about this.
// Category
interface Category<T : Category<T>> {
val id: String
val name: String
val subcategories: List<T>?
}
//default category:
class DefaultCategory(override val name: String) : Category<DefaultCategory> {
override val id: String = "default"
override val subcategories: List<DefaultCategory>? = null
}
//ViewModel
class CategoryViewModel : ViewModel() {
val sourceProperty = SimpleListProperty<Category<*>>()
fun loadData() {
// load items for treeview into 'newItems'
sourceProperty.value = newItems
}
}
// TreeViewFactoryMethod
private fun createTreeView(
listProperty: SimpleListProperty<Category<*>>
): TreeView<Category<*>> {
return treeview {
root = TreeItem(DefaultCategory("Categories"))
isShowRoot = false
root.isExpanded = true
root.children.forEach { it.isExpanded = true }
cellFormat { text = it.name }
populate { parent ->
when (parent) {
root -> listProperty.value
else -> parent.value.subcategories
}
}
}
}
Assuming that on a button click I call viewmodel.loadData(), I would expect the TreeView to update as soon as there's some new data. (If I would've found a way to bind)
I've never had to use bindChildren for TornadoFX before and your use of async isn't very relevant to what I think is your primary problem. So, admittedly, this question kind of confused me at first but I'm guessing you're just wondering why the list isn't appearing in your TreeView? I've made a test example with changes to make it work.
// Category
interface Category<T : Category<T>> {
val id: String
val name: String
val subcategories: List<T>?
}
//default category:
class DefaultCategory(override val name: String) : Category<DefaultCategory> {
override val id: String = "default"
override val subcategories: List<DefaultCategory>? = null
}
//Just a dummy category
class ChildCategory(override val name: String) : Category<ChildCategory> {
override val id = name
override val subcategories: List<ChildCategory>? = null
}
//ViewModel
class CategoryViewModel : ViewModel() {
//filled with dummy data
val sourceProperty = SimpleListProperty<Category<*>>(listOf(
ChildCategory("Categorya"),
ChildCategory("Categoryb"),
ChildCategory("Categoryc"),
ChildCategory("Categoryd")
).asObservable())
fun loadData() {
sourceProperty.asyncItems {
//items grabbed somehow
listOf(
ChildCategory("Category1"),
ChildCategory("Category2"),
ChildCategory("Category3"),
ChildCategory("Category4")
).asObservable()
}
}
}
class TestView : View() {
val model: CategoryViewModel by inject()
override val root = vbox(10) {
button("Refresh Items").action {
model.loadData()
}
add(createTreeView(model.sourceProperty))
}
// TreeViewFactoryMethod
private fun createTreeView(
listProperty: SimpleListProperty<Category<*>>
): TreeView<Category<*>> {
return treeview {
root = TreeItem(DefaultCategory("Categories"))
isShowRoot = false
root.isExpanded = true
root.children.forEach { it.isExpanded = true }
cellFormat { text = it.name }
populate { parent ->
when (parent) {
root -> listProperty
else -> parent.value.subcategories
}
}
}
}
}
There are 2 important distinctions that are important.
1. The more relevant distinction is that inside the populate block, root -> listProperty is used instead of root.listProperty.value. This will make your list appear. The reason is that a SimpleListProperty is not a list, it holds a list. So, yes, passing in a plain list is perfectly valid (like how you passed in the value of the list property). But now that means the tree view isn't listening to your property, just the list you passed in. With that in mind, I would be considerate over the categories' subcategory lists are implemented as well.
2. Secondly, notice the use of asyncItems in the ViewModel. This will perform whatever task asynchronously, then set the items to list on success. You can even add fail or cancel blocks to it. I'd recommend using this, as long/intensive operations aren't supposed to be performed on the UI thread.

Get Enum value by reflection in Kotlin

How would you rewrite the following Java code in Kotlin?
#SuppressWarnings({ "unchecked", "rawtypes" })
static Object getEnumValue(String enumClassName, String enumValue) throws ClassNotFoundException {
Class<Enum> enumClz = (Class<Enum>)Class.forName(enumClassName);
return Enum.valueOf(enumClz, enumValue);
}
The problematic line is Enum.valueOf(enumClz, enumValue)
The automatic conversion from IntelliJ IDE/Android Studio yields the following Enum.valueOf<Enum>(enumClz, enumValue), however there's no such method Enum.valueOf in Kotlin.
Forcing Kotling to use java.lang.Enum: java.lang.Enum.valueOf<Enum>(enumClz, enumValue). Compile error on the generic binding One type argument expected for class Enum<E: Enum<E>>.
Adding the type argument as java.lang.Enum.valueOf<Enum<*>>(enumClz, enumValue) yields a different error: Type argument is not within its bounds. Expected: Enum<Enum<*>!>! Found: Enum<*>.
You could do this in following way, of course you should probably do some additional checks for passed parameters but this should be what you are looking for:
fun getEnumValue(enumClassName: String, enumValue: String): Any {
val enumClz = Class.forName(enumClassName).enumConstants as Array<Enum<*>>
return enumClz.first { it.name == enumValue }
}
Also there is enumValueOf function but there you need to know actual enum type so not sure it helps, anyway here is how you could use that:
enum class SomeEnum{
FIRST, SECOND
}
val enumMember = enumValueOf<SomeEnum>("FIRST")
The best way I have found is to create an interface for the enums, converting them in typed enums:
/**
* Allow to search enums by type
*/
interface TypedEnum<T> {
fun value(): T
companion object {
/**
* Get the value of a typed enum
* #param enumValues array - You can get it with Enum.values()
* #param enumValue to search
* #param defaultValue to return if not found
* #return enum type or default value if not found or first enum value if default value not set
*/
fun <T, E : TypedEnum<T>> getEnumValue(enumValues: Array<E>, enumValue: T, defaultValue: E? = null): E {
try {
return enumValues.first { it.value()?.equals(enumValue) ?: false}
} catch (nsee: NoSuchElementException) {
// Log.e("TYPED_ENUM", "Exception converting value to enum type: $nsee")
}
return defaultValue ?: enumValues.first()
}
}
}
enum class TypeInt: TypedEnum<Int> {
TYPE_1 { override fun value() = 1 },
TYPE_2 { override fun value() = 2 },
TYPE_3 { override fun value() = 3 },
}
enum class TypeString: TypedEnum<String> {
TYPE_1 { override fun value() = "1" },
TYPE_2 { override fun value() = "2" },
TYPE_3 { override fun value() = "3" },
}
#Test
fun getEnumValue_valueExistInt() {
val value = TypedEnum.getEnumValue(TypeInt.values(), 2)
assertEquals(TypeInt.TYPE_2, value)
}
#Test
fun getEnumValue_valueExistString() {
val value = TypedEnum.getEnumValue(TypeString.values(), "2")
assertEquals(TypeString.TYPE_2, value)
}
#Test
fun getEnumValue_valueNotExist() {
val value = TypedEnum.getEnumValue(TypeInt.values(), 0)
assertEquals(TypeInt.TYPE_1, value)
}
#Test
fun getEnumValue_valueNotExistReturnDefault() {
val value = TypedEnum.getEnumValue(TypeInt.values(), 0, TypeInt.TYPE_3)
assertEquals(TypeInt.TYPE_3, value)
}
It is not the most elegant way, but it works. If I find a better solution I update this message.

kotlin - Pass method reference to function

Let's say I have the following Java class:
public class A {
public Result method1(Object o) {...}
public Result method2(Object o) {...}
...
public Result methodN(Object o) {...}
}
Then, in my Kotlin code:
fun myFunction(...) {
val a: A = ...
val parameter = ...
val result = a.method1(parameter) // what if i want methodX?
do more things with result
}
and I want to be able to choose which methodX will be called inside myFunction. in Java, I would pass A::method7 as an argument and call it. in Kotlin it doesn't compile. How should I solve it in Kotlin?
You can also pass the method reference in Kotlin (without needing the heavy hammer that is reflection):
fun myFunction(method: A.(Any) -> Result) {
val a: A = ...
val parameter = ...
val result = a.method(parameter)
do more things with result
}
myFunction(A::method1)
myFunction {/* do something in the context of A */}
This declares method as part of A, meaning you can call it with normal object.method() notation. It Just Works™ with the method reference syntax.
There's also another form that works with the same call syntax, but makes A more explicit:
fun myFunction(method: (A, Any) -> Result) { ... }
myFunction(A::method1)
myFunction {a, param -> /* do something with the object and parameter */}
You can actually do this exactly like you wanted to:
fun myFunction(kFunction: KFunction2<A, #ParameterName(name = "any") Any, Result>) {
val parameter = "string"
val result: Result = kFunction(A(), parameter)
//...
}
myFunction(A::method1)
myFunction(A::method2)

Kotlin, how to retrieve field value via reflection

So I have hundreds of fields in a couple of classes and I'd like to write some methods on them where they automatically println each field and its corresponding value
At the moment I have this:
inner class Version(val profile: Profile) {
#JvmField val MINOR_VERSION = glGetInteger(GL_MINOR_VERSION)
fun write(file: File? = null) {
//file.printWriter().use { out -> out.pri }
this::class.java.fields.forEach {
println(it.isAccessible)
println(it.getInt(it)) }
}
}
But this is what I get:
false
Exception in thread "main" java.lang.IllegalArgumentException: Can not set final int field uno.caps.Caps$Version.MINOR_VERSION to java.lang.reflect.Field
at sun.reflect.UnsafeFieldAccessorImpl.throwSetIllegalArgumentException(UnsafeFieldAccessorImpl.java:167)
at sun.reflect.UnsafeFieldAccessorImpl.throwSetIllegalArgumentException(UnsafeFieldAccessorImpl.java:171)
at sun.reflect.UnsafeFieldAccessorImpl.ensureObj(UnsafeFieldAccessorImpl.java:58)
at sun.reflect.UnsafeQualifiedIntegerFieldAccessorImpl.getInt(UnsafeQualifiedIntegerFieldAccessorImpl.java:58)
Any idea?
Instead of using Java fields and Java reflection code, you can also use Kotlin properties and Kotlin reflection classes:
class Reflector {
val Foo = 1;
fun printFields() {
this::class.memberProperties.forEach {
if (it.visibility == KVisibility.PUBLIC) {
println(it.name)
println(it.getter.call(this))
}
}
}
}
It seems that you are passing the Field variable it as a parameter getInt whereas the parameter should be the object the field belongs to this:
From the Javadoc for Field.getInt(Object obj):
obj - the object to extract the int value from
Perhaps this is what you meant to do:
class Reflector {
#JvmField val Foo = 1;
fun printFields() {
this.javaClass.fields.forEach {
println(it.isAccessible)
println(it.getInt(this))
}
}
}
fun main(args : Array<String>) {
Reflector().printFields()
}

How to get a property name and its value using Swift 2.0, and reflection?

Given this Model:
public class RSS2Feed {
public var channel: RSS2FeedChannel?
public init() {}
}
public class RSS2FeedChannel {
public var title: String?
public var description: String?
public init() {}
}
What would I need to do in order to get the property names and values of an RSS2FeedChannel instance?
Here's what I'm trying:
let feed = RSS2Feed()
feed.channel = RSS2FeedChannel()
feed.channel?.title = "The Channel Title"
let mirror = Mirror(reflecting: feed.channel)
mirror.children.first // ({Some "Some"}, {{Some "The Channel Title...
for (index, value) in mirror.children.enumerate() {
index // 0
value.label // "Some"
value.value // RSS2FeedChannel
}
Ultimately, I'm trying to create a Dictionary that matches the instance, using reflection, but so far I'm unable to get the properties name and values of the instance.
Documentation says that:
The optional label may be used when appropriate, e.g. to represent the name of a stored property or of an active enum case, and will be used for lookup when Strings are passed to the descendant method.
Yet I only get a "Some" string.
Also, the value property is returning a string with the Type RSS2FeedChannel when I would expect each children to be "An element of the reflected instance's structure."!
When i understand correct this should solve ur problem:
func aMethod() -> Void {
let feed = RSS2Feed()
feed.channel = RSS2FeedChannel()
feed.channel?.title = "The Channel Title"
// feed.channel?.description = "the description of your channel"
guard let channel = feed.channel else {
return
}
let mirror = Mirror(reflecting: channel)
for child in mirror.children {
guard let key = child.label else {
continue
}
let value = child.value
guard let result = self.unwrap(value) else {
continue
}
print("\(key): \(result)")
}
}
private func unwrap(subject: Any) -> Any? {
var value: Any?
let mirrored = Mirror(reflecting:subject)
if mirrored.displayStyle != .Optional {
value = subject
} else if let firstChild = mirrored.children.first {
value = firstChild.value
}
return value
}
just some little changes for swift 3:
private func unwrap(_ subject: Any) -> Any? {
var value: Any?
let mirrored = Mirror(reflecting:subject)
if mirrored.displayStyle != .optional {
value = subject
} else if let firstChild = mirrored.children.first {
value = firstChild.value
}
return value
}
You can use the descendent method on the Mirror object to get this information. It will return nil if the values aren't found or the optionals contain no value.
let mirror = Mirror(reflecting: feed.channel)
let child1 = mirror.descendant("Some", "title") // "The Channel Title"
// or on one line
let child3 = Mirror(reflecting: feed).descendant("channel", "Some", "title")

Resources