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.
CodePudding user response:
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>()
}