David Kubecka
11/21/2023, 1:23 PMabstract class MessageProcessor(val supportedTypes: X, val validator: (X) -> Unit) {
fun process(key: String, value: String) {
val messageType = supportedValues.getIfSupported(value)?.let { ... }
if (messageType != null) {
validator(messageType, value)
}
}
}
fun interface X {
fun getIfSupported(value: String): X?
}
Basically I want to init the abstract class with supported values which are then queried to get the value if it's supported or null. The problem is that the supportedTypes typically is an enum. So I thought that I create the X
interface and implement that interface by each concrete enum class. This worked nicely on paper until I realized that I am unable to actually call MessageProcessor constructor because of course I can't instantiate the X
if it's an enum.
So it seems that my original design is flawed but hopefully, the general idea is not. My question is how to best model this in Kotlin.Klitos Kyriacou
11/21/2023, 2:03 PMsupportedValues
and the value returned by supportedValues.getIfSupported
to be the same type? The name supportedValues
sounds like some sort of collection or container, so would be a different type than what getIfSupported
returns.
In the case of enums, consider, for example, Colour.valueOf("RED")
. The Colour
is a class, which can be considered a collection of its instances. If Colour
implements X
, you wouldn't pass Colour
to SomeClass
- you can only pass instances, but you don't want to pass a specific instance. You might want to pass an EnumSet
- that would be a different type than X
.Daniel Pitts
11/21/2023, 3:27 PMRodrigo Munera
11/21/2023, 4:10 PMenum class EClass {
CLASS1, CLASS2
}
abstract class AClass(open val someClass: EClass) {
open fun process(value: String): EClass? {
return null
}
}
class ClassImpl(override val someClass: EClass = EClass.CLASS1): AClass(someClass) {
override fun process(value: String): EClass {
return try {
EClass.valueOf(value)
} catch (e: IllegalArgumentException) {
EClass.CLASS2
}
}
}
I wrote this unit test and it passed based on the behavior ^
import org.junit.jupiter.api.Test
import org.junit.jupiter.api.Assertions.*
class AClassTest {
@Test
fun process() {
val classImpl = ClassImpl(EClass.CLASS1)
val enum: EClass = classImpl.process("CLASS1")
assert(enum == EClass.CLASS1)
val enum2: EClass = classImpl.process("CLASS3")
assert(enum2 == EClass.CLASS2)
}
}
David Kubecka
11/21/2023, 4:10 PMsupportedTypes
and some function, let's say a validator
, which operates on the supported type. That's also the reason why getIfSupported
returns X
- this is a translation from a string to a concrete enum instance.Rodrigo Munera
11/21/2023, 4:15 PMenum class EClass {
CLASS1, CLASS2
}
abstract class AClass(open val someClass: EClass) {
fun process(value: String): EClass? {
return try {
EClass.valueOf(value)
} catch (e: IllegalArgumentException) {
null
}
}
}
Rodrigo Munera
11/21/2023, 4:36 PMtypeof
checking to see if they're supportedRodrigo Munera
11/21/2023, 5:07 PMabstract class MessageProcessor(val supportedTypes: X, val validator: (X, String) -> Unit) {
fun process(key: String, value: String) {
val messageType: X? = supportedTypes.getIfSupported(value)?.let {
// do stuff
null
}
if (messageType != null) {
validator(messageType, value)
}
}
}
interface X {
fun getIfSupported(value: String): X?
}
enum class Message1: X {
TYPE1, TYPE2;
override fun getIfSupported(value: String): X? {
return if (value == this.toString()) {
this
} else {
null
}
}
}
David Kubecka
11/21/2023, 7:18 PMX
param represents a collection of enum values, not a single value. So unfortunately your solution doesn't work.David Kubecka
11/21/2023, 7:23 PMEnumSet
hint. That looks promising. But of course in my abstract class I run into the usual problems with type erasure. I.e. now the parameter becomes
supportedTypes: EnumSet<T>
where T : Enum<T>
and the whole getIfSupported
ideally becomes just
supportedTypes.contains(...)
I would have to somehow convert the input value string into the enum value which seems impossible with the generic enum type.David Kubecka
11/21/2023, 7:25 PMsupportedTypes.map { it.name }.contains(value)
Bad?David Kubecka
11/21/2023, 7:32 PMfun getIfSupported(value: String) =
supportedTypes.singleOrNull { it.name == value }