Daniele Segato
09/11/2018, 7:13 PMcompanion object
into some kind of reusable code?
The main problem is I need the Enum.values()
statically and the class name for strings. I also need to access the jsonValue
in the enum, but I can put it in an interface of the enum I guess, so that's not a big deal...
Any suggestion?
See the code below (please answer in thread)karelpeeters
09/11/2018, 7:16 PMagrosner
09/11/2018, 7:17 PMT: Enum
or something, then move those methods into said class and have the companion implement it.Daniele Segato
09/11/2018, 7:17 PMcompanion object {
val myConverter = Converter(SomeType::class)
}
and use the converter for function and type adapter...
extending on the companion object would just make it cleaner.
even if I try to write a plain class that does that I've issues....
The problem is with the static usage of EnumAndreas Sinz
09/11/2018, 7:20 PMDaniele Segato
09/11/2018, 7:21 PMAndreas Sinz
09/11/2018, 7:22 PMDaniele Segato
09/11/2018, 7:23 PMSomeType
there is SomeOtherType
???
class MyConverter<E: Enum<E>>(val clazz: KClass<E>) {
init {
require(clazz.isInstance(JsonValueEnum::class)) { "Not a JsonValueEnum" }
}
fun convertFromJsonValue(jsonValue: String): E {
return try {
// what should I put here?
???.values().first { (it as JsonValueEnum).jsonValue == jsonValue }
} catch (e: NoSuchElementException) {
throw NoSuchElementException("Cannot find a ${clazz.simpleName} for jsonValue $jsonValue")
}
}
inner class MyAdapter : TypeAdapter<E?>() {
override fun write(output: JsonWriter?, value: E?) {
if (value != null) output?.jsonValue((value as JsonValueEnum).jsonValue) else output?.nullValue()
}
override fun read(input: JsonReader?): E? {
val strValue = input?.nextString()
return if (strValue != null) convertFromJsonValue(strValue) else null
}
}
val GSON_TYPE_ADAPTER by lazy { MyAdapter() }
}
Andreas Sinz
09/11/2018, 7:35 PMinterface JsonValue {
val jsonValue: String
}
interface JsonConverter<T: JsonValue> {
fun getValues(): Array<T>
fun convertFromJsonType(jsonValue: String): T {
return try {
getValues().first { it.jsonValue == jsonValue }
} catch (e: NoSuchElementException) {
throw NoSuchElementException("Cannot find a SomeType for jsonValue $jsonValue")
}
}
}
enum class SomeType(override val jsonValue: String): JsonValue {
// X = enum name, a = value in JSON
X("a"),
Y("b"),
;
// now this is the annoying part...
companion object : JsonConverter<SomeType> {
override fun getValues() = SomeType.values()
}
}
Daniele Segato
09/11/2018, 7:38 PMAndreas Sinz
09/11/2018, 7:54 PMenumValues<T>
with a Function Type, e.g. class MyConverter<E: Enum<E>>(val clazz: KClass<E>, val values: () -> Array<E>) where E: JsonValue
and a factory method inline fun createConverter<reified E: Enum<E>>() where E: JsonValue = MyConverter { enumValues<T>() }
values()
and the compiler knows that every value has a val jsonValue: String
Daniele Segato
09/11/2018, 7:59 PMJsonValue
interface to let me use any name for the value..
So now when I try to use a lambda instead to provide the value through the reified inline function I get:
Illegal usage of inline-parameter 'jsonValue' in 'public inline fun <reified E : Enum<E>> createJsonConverter(jsonValue: (E) -> String): MyConverter<E> defined in YadaYada.kt'. Add 'noinline' modifier to the parameter declaration
inline fun <reified E: Enum<E>> createJsonConverter(jsonValue: (E) -> String): MyConverter<E> {
return MyConverter<E>(E::class, enumValues<E>(), jsonValue)
}
the error on the last parameterAndreas Sinz
09/11/2018, 8:16 PMJsonValueEnum
?Daniele Segato
09/11/2018, 8:46 PMinterface JsonValueEnum {
val jsonValue: String
}
Andreas Sinz
09/11/2018, 8:47 PMenum class SomeType(override val jsonValue: String): JsonValueEnum {
// X = enum name, a = value in JSON
X("a"),
Y("b"),
;
val GSON_TYPE_ADAPTER = createAdapter<SomeType>()
}
class MyAdapter<E: JsonValueEnum>(val values: () -> Array<E>) : TypeAdapter<E?>() {
override fun write(output: JsonWriter?, value: E?) {
if (value != null) output?.jsonValue(value.jsonValue) else output?.nullValue()
}
override fun read(input: JsonReader?): E? {
val strValue = input?.nextString()
return if (strValue != null) convertFromJsonValue(strValue) else null
}
private fun convertFromJsonValue(jsonValue: String): E {
return try {
// what should I put here?
values().first { it.jsonValue == jsonValue }
} catch (e: NoSuchElementException) {
throw NoSuchElementException("Cannot find Enum-Value for jsonValue $jsonValue")
}
}
}
inline fun <reified E> createAdapter() where E: JsonValueEnum, E: Enum<E> = MyAdapter { enumValues<E>() }
Daniele Segato
09/11/2018, 8:49 PMinline fun <reified E: Enum<E>> createAdapter(jsonValue: (E) -> String) = MyAdapter<E>(enumValues<E>(), jsonValue)
jsonValue: (E) -> String
can't be passed to the adapter in an inline function šnoinline
only on the argumentAndreas Sinz
09/11/2018, 8:58 PMDaniele Segato
09/11/2018, 9:00 PMIllegal usage of inline-parameter 'jsonValue' in 'public inline fun <reified E : Enum<E>> createJsonConverter(jsonValue: (E) -> String): MyConverter<E> defined in YadaYada.kt'. Add 'noinline' modifier to the parameter declaration
inline
, noinline
, crossinline
?Andreas Sinz
09/11/2018, 9:33 PMinterface JsonValueEnum {
val jsonValue: String
}
class MyAdapter<E>(val serialize: (E) -> String, val deserialize: (String) -> E) : TypeAdapter<E?>() {
override fun write(output: JsonWriter?, value: E?) {
if (value != null) output?.jsonValue(serialize(value)) else output?.nullValue()
}
override fun read(input: JsonReader?): E? {
val strValue = input?.nextString()
return if (strValue != null) deserialize(strValue) else null
}
}
inline fun <reified E: Enum<E>> createEnumAdapter(noinline serialize: (E) -> String, noinline match: (E, String) -> Boolean): MyAdapter<E> {
return MyAdapter(serialize, { jsonValue -> enumValues<E>().first { match(it, jsonValue) } })
}
inline fun <reified E> createJsonValueEnumAdapter(): MyAdapter<E> where E: Enum<E>, E: JsonValueEnum {
return createEnumAdapter<E>({ it.jsonValue }, { enumValue, stringValue -> enumValue.jsonValue == stringValue })
}
Daniele Segato
09/12/2018, 5:42 PM