3

我的代码

open class Fail(override val message: String, override val cause: Throwable?) : RuntimeException(message, cause)

data class ValidationFail(override val message: String, override val cause: Throwable?) : Fail(message, cause)

将来会在那里定义更多失败

我有 2 个功能

fun fun1(): Either<out Fail, A>
fun fun2(a: A): Either<out Fail, B>

当我尝试像这样调用它们时,fun1().flatMap{fun2(it)} 我得到了

Type mismatch: inferred type is (A!) -> Either<out Fail, B> but ((A!) -> Nothing)! was expected. Projected type Either<out Fail, A> restricts use of public final fun <U : Any!> flatMap(p0: ((R!) -> Either<L!, out U!>!)!): Either<L!, U!>! defined in io.vavr.control.Either

来自 vavr 的代码

default <U> Either<L, U> flatMap(Function<? super R, ? extends Either<L, ? extends U>> mapper) {
    Objects.requireNonNull(mapper, "mapper is null");
    if (isRight()) {
        return (Either<L, U>) mapper.apply(get());
    } else {
        return (Either<L, U>) this;
    }
}

我猜 o 有这个错误,因为L在 flatMap 定义中没有? extends L

有什么解决方法吗?

4

1 回答 1

0

在您的特定情况下,您可以通过从返回类型中删除out差异来使其编译。无论如何,您都不应该使用通配符类型作为返回类型fun1fun2

fun1但是,如果您以这种方式定义并定义它,这将无济于事fun2

fun fun1(): Either<ConcreteFail1, A>
fun fun2(a: A): Either<ConcreteFail2, B>

替换L? extends LinflatMap签名也无济于事,因为ConcreteFail2它不是ConcreteFail1. 问题是它Either应该是协变的,但在 Java中不存在声明站点差异之类的东西。虽然有使用方法的解决Either#narrow方法:

Either.narrow<Fail, A>(fun1()).flatMap { Either.narrow(fun2(it)) }

当然,它看起来很奇怪,必须提取到单独的扩展函数中:

inline fun <L, R, R2> Either<out L, out R>.narrowedFlatMap(
    crossinline mapper: (R) -> Either<out L, out R2>
): Either<L, R2> = narrow.flatMap { mapper(it).narrow }

在哪里narrow

val <L, R> Either<out L, out R>.narrow: Either<L, R> get() = Either.narrow(this)

我认为Vavr没有提供自己的narrowedFlatMap,因为这种方法需要使用通配符接收器类型,所以它不能是成员方法,必须是静态方法,这破坏了操作流水线的所有可读性:

narrowedFlatMap(narrowedFlatMap(narrowedFlatMap(fun1()) { fun2(it) }) { fun3(it) }) { fun4(it) }

但由于我们使用 Kotlin,我们也可以流水线化静态(扩展)函数:

fun1().narrowedFlatMap { fun2(it) }.narrowedFlatMap { fun3(it) }.narrowedFlatMap { fun4(it) }
于 2019-11-16T21:18:43.570 回答