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.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With