https://kotlinlang.org logo
#getting-started
Title
# getting-started
d

dave08

01/24/2022, 3:11 PM
How could I get the enum value using Android WorkManager's Data::getString's value:
Copy code
@ExperimentalStdlibApi
inline fun <reified T> Data.getValue(obj: Any?, prop: KProperty<*>): T {
    return when(T::class) {
        String::class -> getString(prop.name) as T
        Int::class -> getInt(prop.name, -1) as T
        Boolean::class -> getBoolean(prop.name, false) as T
        Enum::class -> getString(prop.name)?.let { enumValueOf<T>(it) } // This doesn't compile...
        else -> error("Trying to retrieve invalid type in Data for ${prop.name}: ${T::class}")
    }
}
The goal is to have something like:
Copy code
enum Foo { BAZ, BAR }

class SomeData(val inputData: Data) {
   val dataEnumKeyForFoo: Foo by inputData // Saved as a string in Data with the key `dataEnumKeyForFoo` and the value one of the enum value's names...
}
j

Javier

01/24/2022, 3:22 PM
obj
is used for something?
j

Joffrey

01/24/2022, 3:22 PM
I don't believe you can get type inference from this kind of
when
on the class of
T
(even with a reified
T
). This is why you needed to cast in the other cases. I think you could use delegate providers instead of direct delegates. And for instance provide one for primitives and one for enums (with extra constraint on
T : Enum<T>
which will allow
enumValueOf
):
Copy code
class SomeData(val inputData: Data) {
   val dataEnumKeyForFoo: Foo by inputData.enum()
   val otherProp: Int by inputData.primitive()
}
But you could even go as far as creating one for each primitive type too
@Javier I think that
obj
is necessary to conform to the delegate contract, even if not used
j

Javier

01/24/2022, 3:23 PM
but what is the type?
j

Joffrey

01/24/2022, 3:23 PM
The type of what?
j

Javier

01/24/2022, 3:24 PM
because you, maybe, can
Copy code
when(obj) {
    is String -> ... as T
    is Enum<*> -> ... as T
    else -> ... as T
}
j

Joffrey

01/24/2022, 3:24 PM
I think
obj
is the instance of the class in which the property is declared, so it's not of type
T
. In the given example,
obj
would be of type
SomeData
👍 1
👍🏼 1
d

dave08

01/24/2022, 3:26 PM
obj
isn't needed, since such a class just contains a Data instance... the truth is, if I make that
obj
a
DataProvider
then I wouldn't need the receiver to be
Data
I could retrieve it from
obj
, but I don't think that would help too much...
So
enum()
would return a
ReadWriteProperty
implementation...?
I guess I can't avoid creating one instance per property per class...?
I can't use enumValueOf with (since it's not reified):
Copy code
class EnumDataProperty<T : Enum<T>>(): ReadWriteProperty<Data, T> {
    override fun getValue(thisRef: Data, property: KProperty<*>): T {
        return thisRef.getString(property.name)?.let { enumValueOf(it) }!!
    }

    override fun setValue(thisRef: Data, property: KProperty<*>, value: T) {
        TODO("Not yet implemented")
    }
}
I guess not everything is so simple with delegate properties... set wouldn't work because it's a Data.Builder... I ended up with:
Copy code
inline fun <reified T : Enum<T>> Data.getEnum(key: String): T? =
    getString(key)?.let { enumValueOf<T>(it) }

inline fun <reified T : Enum<T>> Data.Builder.putEnum(key: String, value: T): Data.Builder {
    putString(key, value.name)

    return this
}

inline fun buildData(configure: Data.Builder.() -> Data.Builder): Data = Data.Builder().apply {
    configure()
}.build()
53 Views