Using the chain of responsibility pattern I ran into a problem where next chain element was expected to have the same generic type of the first element. I know why this happens: The first handler expects the second handler to use the generic type "Apple". I just don't know exactly how to solve it.
There is an answer on how to handle it in java here, but since java doesn't have reified types and all that the approach should look different in Kotlin, right?
There are different options that come to my mind:
- Don't use generics - would lead to casting the collection types to specific subtypes and wouldn't look clean
- Try to use reified types (how?)
To illustrate the problem I am posting a demo code below.
data class Apple(val name:String, val color:Int)
data class Orange(val circumference:Double)
object Main{
@JvmStatic
fun main(args: Array<String>) {
val first = FirstHandler()
val second = SecondHandler()
first.setNextHandler(second) // !!! wrong type here since <Apple> is expected
first.process()
}
}
abstract class ChainHandler<T>{
protected var nextHandlerInChain:ChainHandler<T>? = null
fun setNextHandler(handler: ChainHandler<T>): ChainHandler<T> {
this.nextHandlerInChain = handler
return handler
}
abstract fun peel(): Collection<T>
abstract fun process():MutableMap<String,Any> }
class FirstHandler : ChainHandler<Apple>() {
override fun peel(): Collection<Apple> {
return Collections.emptyList<Apple>()
}
override fun process(): MutableMap<String, Any> {
val peeledApples = peel()
val map = nextHandlerInChain?.process()
map?.put("apples",peeledApples) ?:kotlin.run {
val map = mutableMapOf<String,Any>()
map.put("apples",peeledApples)
}
return map!!
} }
class SecondHandler : ChainHandler<Orange>() {
override fun peel(): Collection<Orange> {
return Collections.emptyList<Orange>()
}
override fun process(): MutableMap<String, Any> {
val peeledOranges = peel()
val map = nextHandlerInChain?.process()
map?.put("oranges",peeledOranges) ?:kotlin.run {
val map = mutableMapOf<String,Any>()
map.put("oranges",peeledOranges)
}
return map!!
}
}
Kotlin has something called a star projection that might help you here. It basically tells the compiler that you don't really care what type of
ChainHandler
you get. You can use it to make yoursetNextHandler
compile, like this:You can read more about star projections here: https://kotlinlang.org/docs/reference/generics.html#star-projections
As for reifying the type parameter: Reified type parameters work only for inline functions. Not for type parameters on a class.