在Kotlin中实现右偏Either类型时的协变类型问题
解决方法
错误的核心原因是:L被声明为out协变类型,但在flatMap的参数f的返回类型Either<L, R2>中,L处于逆变位置——函数类型(R) -> Either<L, R2>对于L是逆变的,而协变类型参数不允许出现在逆变位置。
以下是两种仅通过类型调整解决问题的方案,无需外部函数或when语句:
方案一:将f的返回类型声明为Either<out L, R2>
通过在f的返回类型中给L添加out修饰,明确允许返回L的子类类型的Either,避免L出现在逆变位置:
sealed class Either<out L, out R> { abstract fun isLeft(): Boolean abstract fun isRight(): Boolean // 修改flatMap的参数f的返回类型为Either<out L, R2> abstract fun <R2> flatMap(f: (R) -> Either<out L, R2>): Either<L, R2> fun <R2> map(f: (R) -> R2): Either<L, R2> = flatMap { Right(f(it)) } } data class Left<L>(val l: L): Either<L, Nothing>() { override fun isLeft(): Boolean = true override fun isRight(): Boolean = false override fun <R2> flatMap(f: (Nothing) -> Either<out L, R2>): Either<L, R2> = this } data class Right<R>(val r: R): Either<Nothing, R>() { override fun isLeft(): Boolean = false override fun isRight(): Boolean = true override fun <R2> flatMap(f: (R) -> Either<out Nothing, R2>): Either<Nothing, R2> = f(r) }
方案二:引入新的逆变类型参数L2
通过声明一个新的类型参数L2并限制它为L的子类,让f返回Either<L2, R2>,而flatMap的返回类型保持Either<L, R2>(利用L的协变性,Either<L2, R2>可安全转换为Either<L, R2>):
sealed class Either<out L, out R> { abstract fun isLeft(): Boolean abstract fun isRight(): Boolean // 新增L2类型参数,限制为L的子类 abstract fun <L2 : L, R2> flatMap(f: (R) -> Either<L2, R2>): Either<L, R2> fun <R2> map(f: (R) -> R2): Either<L, R2> = flatMap { Right(f(it)) } } data class Left<L>(val l: L): Either<L, Nothing>() { override fun isLeft(): Boolean = true override fun isRight(): Boolean = false override fun <L2 : L, R2> flatMap(f: (Nothing) -> Either<L2, R2>): Either<L, R2> = this } data class Right<R>(val r: R): Either<Nothing, R>() { override fun isLeft(): Boolean = false override fun isRight(): Boolean = true override fun <L2 : Nothing, R2> flatMap(f: (R) -> Either<L2, R2>): Either<Nothing, R2> = f(r) }
两种方案都能保留Either的协变性,同时让flatMap的类型签名符合Kotlin的泛型规则,且不需要依赖外部函数或when语句实现逻辑。
内容的提问来源于stack exchange,提问作者jorexe
相关产品推荐
相关产品推荐

