减少其中一个州抛出异常的简明方法

时间:2018-01-29 15:07:48

标签: java java-stream reduce fold vavr

我有很多这些:

Validation<String, Foo> a;
Validation<String, Foo> b;
Validation<String, Foo> c;

以下是他们的一些方法:

boolean isValid();
boolean isInvalid(); // === !isValid()
String getError();

现在,我试图这样做:

Stream.of(a, b, c).reduce(
    Validation.valid(foo),
    (a, b) -> a.isValid() && b.isValid()
              ? Validation.valid(foo)
              : String.join("; ", a.getError(), b.getError())
);

明显的问题是,如果只有ab中的一个出错,那么就会出现一个不必要的;。但是有一个更严重的问题:如果验证有效,getError()会抛出异常。

有没有办法可以编写这个lambda(或使用io.vavr.control.Validation library中的其他内容)而不会全部4个案例(a && ba && !b!a && b,{ {1}})明确吗?

修改

为了更清楚,我最终想要!a && !b的结果。我认为它的行为就像一个“monad”,但我不确定。

4 个答案:

答案 0 :(得分:1)

我认为您尝试实现的目标在Validation域中更容易解决。

首先,将您的Either流转换为Stream<Either<String, Foo>> eithers = Stream.of(a, b, c) .map(Validation::toEither); s的流:

Either<String, Foo> result = Either.sequence(eithers)
    .mapLeft(seq -> seq.collect(Collectors.joining("; ")))
    .map(combinator); // fill in with combinator function that converts
                      // a Seq<Foo> into a single Foo

然后合并它们:

Foo

由于您没有指定如何将多个有效Either.Left个对象合并为一个,我将其打开以供您填写上述示例中的组合函数。

Either.sequence(...)如果任何提供的eithers为左侧,则返回包含左侧值序列的Either.Right,或者{Validation.sequence(...)会将多个eithers减少为一个{1}}包含一个(可能是空的)所有正确值的序列,如果提供的eithers的 none 是左侧。

更新

有一个{{3}}方法可以在不转换为Either域的情况下执行此操作(我在创建原始答案时不知何故错过了 - 感谢指出):

Validation<Seq<String>, Seq<Foo>> validations = Validation.sequence(
        Stream.of(a, b, c)
            .map(v -> v.mapError(List::of))
);

Validation<String, Foo> result = validations
    .mapError(errors -> errors.collect(Collectors.joining("; ")))
    .map(combinator); // fill in with combinator function that converts
                      // a Seq<Foo> into a single Foo

您说Foo个实例是相同的,这意味着您可以使用Seq::head代替组合函数。但是你需要注意不要使用空的验证序列作为输入,因为在这种情况下Seq::head会抛出NoSuchElementException

答案 1 :(得分:0)

如您所见,import re text = 'Version: 10.2.4.110\r\nPkg name: XXXX-10.2.4-Agent-Linux-x86_64\r\nRevision: 110\r\nPatch version: 23' replaced = re.sub(r'(?:Version:\s?((?:\d+\.){3})(?:[^\r\n]+\r?\n){3}Patch version:\s?(\d+))', '\g<1>\g<2>', text) print(replaced) //10.2.4.23 的输出为字符串,错误列表以reduce分隔。

您正在混合累加器参数:

  • ;是当前减少的部分结果
  • a是您正在迭代的对象本身

我会做这样的事情:

b

答案 2 :(得分:0)

Collectors.groupingBy()怎么样?我认为你仍然可以改进字符串的错误部分并加入它们,下面代码的输出是:

{false=[SomeException, ReallyBadProblem], true=[3, 5]}

代码示例:

import static java.util.stream.Collectors.groupingBy;
import static java.util.stream.Collectors.mapping;
import static java.util.stream.Collectors.toSet;

import java.util.Set;
import java.util.TreeMap;
import java.util.stream.Stream;

public class Main {


    public static void main(String[] args)  {

        Validation<String,Integer> a = new Validation<>(true, null, 3);
        Validation<String,Integer> b = new Validation<>(true, null, 5);
        Validation<String,Integer> c = new Validation<>(false, "SomeException", null);
        Validation<String,Integer> d = new Validation<>(false, "ReallyBadProblem", null);

        //Stream.of(a,b,c).collect(Collectors.groupingBy(v->v.isValid(), v->v.));

        TreeMap<Boolean, Set<Object>> map = Stream.of(a,b,c,d).collect((groupingBy(v->v.isValid(), TreeMap::new,
                                             mapping(v-> { return v.isValid() ? v.valid() : v.getError();}, toSet()))));

        System.out.println(map);
    }

    public static class Validation<E, T>{

        boolean valid;
        T validVal;
        String error;


        public Validation(boolean valid, String error, T validVal) {
            super();
            this.valid = valid;
            this.error = error;
            this.validVal = validVal;
        }
        /**
         * @return the valid
         */
        public boolean isValid() {
            return valid;
        }
        /**
         * @param valid the valid to set
         */
        public void setValid(boolean valid) {
            this.valid = valid;
        }
        /**
         * @return the error
         */
        public String getError() {
            return error;
        }
        /**
         * @param error the error to set
         */
        public void setError(String error) {
            this.error = error;
        }

        public T valid() {
            return validVal;
        }


    }
}

答案 3 :(得分:0)

这是我结束的方式,虽然我认为@Nandor(接受的答案)说得对。 (他的解决方案基于io.vavr.control.Validation的更新版本而不是我可以使用的版本(仍为javaslang.control.Validation)。我发现mapLeft已重命名为mapErrors但是,有一些与Seq操作相关的缺失位。由于缺乏对Java的熟悉,我无法解决我在这条路线上的错误。)

Validation<String, AdRequest> validation =
    Stream.of(
        validateTargetingRequest(adRequest),
        validateFlightRequest(adRequest, ad),
        validateCreativeRequest(adRequest)
    ).reduce(
        Validation.valid(adRequest),
        (a, b) -> {
          if (a.isValid() && b.isValid()) {
            return Validation.valid(adRequest);
          }
          if (a.isInvalid() && b.isInvalid()) {
            return Validation.invalid(String.join("; ", a.getError(), b.getError()));
          }
          // This seemingly overcomplicated structure was necessitated by the fact that getError
          // throws an exception when called on an Valid form of Validation.
          return a.isInvalid() ? a : b;
        }
    );