Tanguy Retail
08/24/2023, 12:43 PMsealed class Input
data object InputTypeA : Input()
interface Output
data object OutputTypeA : Output
interface Handler<in I : Input, out O : Output> {
fun handle(input: I): O
}
class HandlerA : Handler<InputTypeA, OutputTypeA> {
override fun handle(input: InputTypeA): OutputTypeA = OutputTypeA
}
class Controller(private val handlerA: HandlerA) {
fun <I : Input, O : Output> selectHandler(input: I): Handler<I, O>? =
when (input) {
/*
* Type mismatch.
* Required:
* Handler<I, O>?
* Found:
* HandlerA
*/
is InputTypeA -> handlerA
else -> null
}
}
Sam
08/24/2023, 1:14 PMHandler<I, O>
. You certainly can’t prove anything about O
, so you should remove that parameter from the function signature: it doesn’t do anything. But you can’t prove that handlerA
is a Handler<I, *>
either. The problem arises from the fact that the actual value of the generic type is not necessarily the exact type of the value parameter. Determining the type of input
does not actually tell us the value of I
. For example, consider that a consumer would be able to call controller.handleInput<Input>(someInputA)
and get a result of type Handler<Input, *>
, even though the actual handler can only accept InputTypeA
.
My advice would be to make a selectAndHandle
function that both selects and invokes the handler.Sam
08/24/2023, 1:30 PMinterface Input<in I : Input<I>> {
fun getHandler(controller: Controller): Handler<I>
}
data object InputTypeA : Input<InputTypeA> {
override fun getHandler(controller: Controller): Handler<InputTypeA> = controller.getHandler(this)
}
data object InputTypeB : Input<InputTypeB> {
override fun getHandler(controller: Controller): Handler<InputTypeB> = controller.getHandler(this)
}
interface Handler<in I : Input<I>> {
fun handle(input: I)
}
class Controller(
private val handlerA: Handler<InputTypeA>,
private val handlerB: Handler<InputTypeB>
) {
fun <I: Input<I>> getHandler(input: Input<I>): Handler<I> = input.getHandler(this)
fun getHandler(input: InputTypeA): Handler<InputTypeA> = handlerA
fun getHandler(input: InputTypeB): Handler<InputTypeB> = handlerB
}
Sam
08/24/2023, 1:30 PMSam
08/24/2023, 1:38 PMclass Controller(private val handlerA: HandlerA) {
fun handle(input: Input): Output? =
when (input) {
is InputTypeA -> handlerA.handle(input)
else -> null
}
}
Tanguy Retail
08/24/2023, 2:14 PMfun selectAndHandle(input: Input): Output =
when (input) {
is InputTypeA -> handlerA
is InputTypeB -> handlerB
}.handle(input) // But obviously this is not gonna work!
Any thought? 🤔Sam
08/24/2023, 2:16 PMinput
is only applied inside the relevant branch of the when
statementSam
08/24/2023, 2:17 PMwhen
block, you go back to having an unknown type of input again, at least as far as the compiler is concernedTanguy Retail
08/24/2023, 2:38 PM