How to use sealed classes with generics in kotlin`

893 views Asked by At

I have the following classes but I'm struggling with generics


sealed class Result<T,E> {
    data class Success<T>(val data: T): Result<T,Void>()
    data class Failure<E>(val error: E): Result<Void,E>()
}


fun interface SeqListener<T,E> {
    fun onComplete(result: Result<T,E>)
}

abstract class Seq<T,E> {

    var started = false
        private set

    var complete = false
        private set

    private lateinit var seqListener: SeqListener<T,E>

    fun start(listener: SeqListener<T,E>) {
        if (!started) {
            seqListener = listener
            started = true
            return doStart()
        } else throw IllegalStateException("cannot call start on a sequence more than once")
    }

    fun success(result: Result.Success<T>) {
        complete(result)
    }

    fun fail(result: Result.Failure<E>) {
        complete(result)
    }

    private fun complete(result: Result<T,E>) {
        if (!complete) {
            complete = true
            seqListener.onComplete(result)
        } else throw IllegalStateException("cannot call success or fail multiple times")
    }

    abstract fun doStart()
}

but the compiler complains that the calls to: complete(result) for fail/success above are of the wrong type Required: Result<T,E> Found: Result.Success<T> for the one from success.

If I change the complete method to: complete(result: Result<*,*>) then those errors go away but the call to seqListener.onComplete(result) fails with Required: Result<T,E> Found: Result<*,*>

Any help would be appreciated.

1

There are 1 answers

4
Tenfour04 On BEST ANSWER

Define your types as out and use Nothing for the unused ones. This will make the compiler very lenient about acceptable upcasting.

The Nothing type can be thought of as the opposite of Any, because it is logically treated as a subtype of everything. So with covariant out types, it can be implicitly upcast to anything.

sealed class Result<out T, out E> {
    data class Success<T>(val data: T): Result<T, Nothing>()
    data class Failure<E>(val error: E): Result<Nothing, E>()
}