Get Enum value by reflection in Kotlin - reflection

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.

Related

Kotlin 2-way binding custom view

I have 1 custom view that extends ConstraintLayout and contains 1 EditText and 2 TextViews
On my custom view i define this attr (and others) :
<attr name="Text" format="string" />
and i use it like :
app:Text="#={login.email}"
Inside my custom view i define :
companion object {
#JvmStatic #BindingAdapter("Text")
fun setText(nMe : View, nText: String) {
nMe.nInput.setText(nText)
}
#InverseBindingAdapter(attribute = "Text")
fun getText(nMe : View) : String {
return nMe.nInput.text.toString()
}
witch works fine in one-way binding
app:Text="#{login.email}"
But when i try to use it in 2-way binding i get erros pointing to ActivityLoginBinding.java java.lang.String callbackArg_0 = mBindingComponent.null.getText(mEmail);
What to do to get 2-way binding?
L.E : After some research i end up with this :
#InverseBindingMethods(InverseBindingMethod(type =
CustomInput::class,attribute = "bind:Text",event =
"bind:textAttrChanged",method = "bind:getText"))
class CustomEditTextBinder {
companion object {
#JvmStatic
#BindingAdapter(value = ["textAttrChanged"])
fun setListener(editText: CustomInput, listener: InverseBindingListener?) {
if (listener != null) {
editText.nInput.addTextChangedListener(object : TextWatcher {
override fun beforeTextChanged(charSequence: CharSequence, i: Int, i1: Int, i2: Int) {
}
override fun onTextChanged(charSequence: CharSequence, i: Int, i1: Int, i2: Int) {
}
override fun afterTextChanged(editable: Editable) {
listener.onChange()
}
})
}
}
#JvmStatic
#InverseBindingAdapter(attribute = "Text")
fun getText(nMe: CustomInput): String {
return nMe.nInput.text.toString()
}
#JvmStatic
#BindingAdapter("Text")
fun setText(editText: CustomInput, text: String?) {
text?.let {
if (it != editText.nInput.text.toString()) {
editText.nInput.setText(it)
}
}
}
}
}
But right now i get :
Could not find event TextAttrChanged
I think all you need is event = "android:textAttrChanged".
This works for me (set text to empty String if it is 0):
object DataBindingUtil {
#BindingAdapter("emptyIfZeroText")
#JvmStatic
fun setText(editText: EditText, text: String?) {
if (text == "0" || text == "0.0") editText.setText("") else editText.setText(text)
}
#InverseBindingAdapter(attribute = "emptyIfZeroText", event = "android:textAttrChanged")
#JvmStatic
fun getText(editText: EditText) = editText.text.toString()
}

Check that the class implements the interface. Kotlin

I want check that the child class of SignalPayload implements the interface IConvertible. How can I do that?
sealed class SignalPayload {
companion object {
fun trueTypeInstance(type: KClass<SignalPayload>) : SignalPayload? {
// if (*** __ KClass<SignalPayload> implemented IConvertible ___ **)
......
}
}
}
object Empty : SignalPayload()
data class BadData(val message: String = "BAD") : SignalPayload() {
override fun toString(): String {
return message
}
}
data class payloadString(private var payload: String = "") : SignalPayload(), IConvertible
data class payloadInt(private var payload: Int = 0) : SignalPayload(), IConvertible
data class payloadFloat(private var payload: Float = 0F) : SignalPayload(), IConvertible
data class payloadBool(private var payload: Boolean = false) : SignalPayload(), IConvertible
interface IConvertible {
val asBool: Boolean
val asInt: Int
val asFloat: Float
val asString: String
fun setFromPayload(data: IConvertible)
}
How can I do that?
UPD
That I had think use it.
Class SignalChannel know what field mPayload needed (through property val type: ???). At first mPayload equal Empty
interface IArriving {
fun onNewPayload(data: SignalPayload, sender: IArriving?)
}
interface IConvertible {
val asBool: Boolean
val asInt: Int
val asFloat: Float
val asString: String
fun setFromPayload(data: IConvertible)
}
class SignalChannel(val idx: Int, val type: ???, val name: String = "") : IArriving {
var mPayload: SignalPayload = Empty
// when new data arriving - need converting for `type` and refresh field mPayload
override fun onNewPayload(data: SignalPayload, sender: IArriving?) {
if ( mPayload is Empty ) {
mPayload = SignalPayload.trueTypeInstance(type) // that's my problem
}
mPayload.setFromPayload(data)
}
}
You could make use of proper generics in order to confine the method to types that are both SignalPayload and IConverible:
fun <T> trueTypeInstance(): SignalPayload?
where T : IConvertible, T : SignalPayload {
TODO()
}
Called like this:
SignalPayload.trueTypeInstance<payloadString>() //OK
SignalPayload.trueTypeInstance<Empty>() //Fails
SignalPayload.trueTypeInstance<String>() //Fails
Not sure about your use case though...

Kotlin get Field Annotation always empty

I have the following Kotlin Annotation
#Target(AnnotationTarget.FIELD, AnnotationTarget.PROPERTY_GETTER)
#Retention(AnnotationRetention.RUNTIME)
annotation class Field(val value: String)
And the following Test Code
class TestObject(#field:Field("id") val id: Long) {
#field:Field("string")
val string = "Hello world"
#get:Field("prop")
val prop get() = string
}
class AnnotationTest {
#Test
fun test() {
val obj = TestObject(200L)
for (member in obj::class.declaredMemberProperties) {
if (member.findAnnotation<Field>() != null) {
println(member)
}
println(member)
println(member.annotations)
}
println("#########")
for(member in obj.javaClass.declaredFields) {
println(member)
println(member.annotations)
}
}
}
It will print the following Output:
val TestObject.id: kotlin.Long
[]
val TestObject.prop: kotlin.String
[]
val TestObject.string: kotlin.String
[]
#########
private final java.lang.String TestObject.string
[Ljava.lang.annotation.Annotation;#33d512c1
private final long TestObject.id
[Ljava.lang.annotation.Annotation;#515c6049
Why I can't see the Annotation with Kotlin reflection? Need to find out if the given annotation is present on fields and property getters.
Your annotation for prop is targeting getter, instead of calling findAnnotation on the property, you have to call it on the getter of the property.
for (member in obj::class.declaredMemberProperties) {
if (member.getter.findAnnotation<Field>() != null) { //add .getter
println(member)
}
println(member)
println(member.getter.annotations) //add .getter
}
Your annotation for id and string is targeting field, so what you did in the second loop is correct. Since member.annotations returns Annotation[], you have to change it to a List before printing it.
for(member in obj.javaClass.declaredFields) {
println(member)
println(member.annotations.toList()) //Add .toList()
}
Output:
val TestObject.id: kotlin.Long
[]
val TestObject.prop: kotlin.String
val TestObject.prop: kotlin.String
[#Field(value=[prop])]
val TestObject.string: kotlin.String
[]
#########
private final java.lang.String TestObject.string
[#Field(value=[string])]
private final long TestObject.id
[#Field(value=[id])]

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 can I get the name of a Kotlin property?

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.

Resources