y
09/28/2023, 8:51 AMsealed class Thing {
class A : Thing()
class B : Thing()
class C : Thing()
}
class MyError
sealed class MyResult<out T, out E> {
data class Ok<T>(val t: T) : MyResult<T, Nothing>()
data class Err<E>(val e: E) : MyResult<Nothing, E>()
}
fun returnsTheSameSubclass(thing: Thing): MyResult<Thing, MyError>
sorry for the contrived example.
is there any way to define returnsTheSameSubclass()
to make Kotlin understand that the subclass of the input is the same as the one in the output? something like,
fun <X: Thing> returnsTheSameSubclass(thing: X): MyResult<X, MyError>
(which I couldn't get to work)Joffrey
09/28/2023, 8:53 AMy
09/28/2023, 8:59 AMsealed interface MyInterface
that a subset of the subclasses in Thing
implement (and nothing else).
we have a function
fun returnsTheSameSubclass(thing: Thing): MyResult<Thing, MyError> = when (thing) {
is A -> fa(thing)
is B -> fb(thing)
is C -> fc(thing)
}
fun fa(a: A): MyResult<A, MyError>
fun fb(b: B): MyResult<B, MyError>
fun fc(c: C): MyResult<C, MyError>
and now I'd like to create a function that takes a MyInterface
, runs it through returnsTheSameSubclass
, and returns a MyResult<MyInterface, MyError>
.Thing
-> call function -> explicitly downcast to MyInterface
Wout Werkman
09/28/2023, 9:42 AMfun <X: Thing> returnsTheSameSubclass(thing: X): ... {
when (thing) {
is A -> { /* this scope */ }
else -> { /* necessary because issue linked below */ }
}
}
Kotlin smart casts thing, but in that scope the Kotlin compiler has not inferred that that generic X
is of type A
in this scope.
As a workaround you can change fa
to use a generic in and output. In this case, Kotlin can infer that fa
returns the X
defined in returnsTheSameSubclass
. (Kotlin will report a warning for defining X: A
because it's final, even though the generic constraint changes the semantics)
Here is a example that works using the workaround.
fun <X: Thing> returnsTheSameSubclass(thing: X): MyResult<X, MyError> = when (thing) {
is Thing.A -> fa(thing)
is Thing.B -> TODO()
is Thing.C -> TODO()
else -> TODO("This else is here because of KT-21908")
}
@Suppress("FINAL_UPPER_BOUND")
fun <X : Thing.A> fa(thing: X): MyResult<X, MyError> = MyResult.Ok(thing)
(Why you need else for when statement containing a X: Thing
)y
09/28/2023, 10:11 AM