https://kotlinlang.org logo
#getting-started
Title
# getting-started
y

y

09/28/2023, 8:51 AM
Copy code
sealed 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)
j

Joffrey

09/28/2023, 8:53 AM
What "didn't work" in your attempt? This is the correct way of achieving the effect you wanted.
y

y

09/28/2023, 8:59 AM
yeah, I can't explain this without actually showing the issue. so there's a
sealed interface MyInterface
that a subset of the subclasses in
Thing
implement (and nothing else). we have a function
Copy code
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>
.
and I'm wondering if there's a way to do this without upcast to
Thing
-> call function -> explicitly downcast to
MyInterface
w

Wout Werkman

09/28/2023, 9:42 AM
Yeah the problem is that in the following scope:
Copy code
fun <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.
Copy code
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

y

09/28/2023, 10:11 AM
@Wout Werkman thanks a lot!