Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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<*>.

like image 410
m0skit0 Avatar asked Sep 13 '18 14:09

m0skit0


2 Answers

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")
like image 162
FilipRistic Avatar answered Oct 11 '22 06:10

FilipRistic


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.

like image 37
J.S.R - Silicornio Avatar answered Oct 11 '22 05:10

J.S.R - Silicornio