【问题标题】:How to implement this nested flow with optionals?如何使用选项实现这个嵌套流?
【发布时间】:2017-04-01 06:34:50
【问题描述】:

我有一个将String 作为输入的方法,还应该返回一个String

以下 ASCII 艺术呈现逻辑流程:

Option<A> optA = finder.findA(input);

          optA
           /\
isEmpty() /  \ isDefined()  
         /    \
 "ERR_1"       Option<B> optB = finder.findB(optA.get().bid);
                      / \
           isEmpty() /   \ isDefined()
                    /     \
                "ERR_2"    opt2.get().id

基本上对于给定的input,我正在寻找A 对象,该对象以Option 形式返回。然后是A 存在我正在寻找B - 也包裹在Option 中,否则返回ERR_1。然后如果存在B,则返回它的id,否则返回ERR_2

我想知道如何以一种简洁明了的方式(没有任何 ifology)使用可选项(或者可能是模式匹配?)来实现它 - 可能是单行。

谁能给点建议?

可以在here找到试用的源代码。

【问题讨论】:

  • Optional&lt;A&gt;Optional&lt;B&gt; 彼此不相关。你是说Optional&lt;A&gt;的存在取决于Optional&lt;B&gt;的存在?
  • @Makoto: Optional&lt;A&gt; 可能存在,即使Optional&lt;B&gt; 不存在。
  • 我现在看到了;我看着图表很有趣。

标签: java functional-programming java-8 optional vavr


【解决方案1】:

由于您使用的是 javaslang,Try 似乎是一个更好的选择,因为它通过链传播错误,而 Option 仅传播其“空虚”。

如果您可以将findAfindB 更改为返回Try,您会得到:

Try<B> b = finder.findA(input)
    .flatMap(a -> finder.findB(a.bid))

如果你不能,那么:

Try<B> b = finder.findA(input).toTry(() -> new Exception("ERR_1"))
    .flatMap(a -> findB(a.bId).toTry(() -> new Exception("ERR_2")))

得到一个暂定的B,我不确定你是否想将有效值和错误折叠成相同的值,如果是这样的话:

String value = b.getOrElseGet(Throwable::getMessage)

如果您在创建无意义的异常时遇到问题,您可以在每个查找操作中使用Either,其中左边的值是您的错误类型。这似乎更好地模拟了问题,但可能具有较长类型签名的缺点,具体取决于您如何拆分表达式。​​

【讨论】:

    【解决方案2】:

    看起来您有 3 个可能的退出点:

    1. optA 为空 -> “ERR_1”
    2. optA 不为空 && optB 为空 -> "ERR_2"
    3. 两者都不为空 -> optB.get().bid

    您可以通过使用 Javaslang 来实现这一点:

     optA
       .map(a -> finder.findB(a.bid)
          .map(b -> b.bid)
          .getOrElse("ERR_2"))
       .getOrElse("ERR_1");
    

    如果optA为空,我们将直接跳转到orElse("ERR_1")

    如果optA 不为空,我们将使用存储在其中的值来获取b.bid"ERR_2" 的值,以防optB 为空。

    此外,在纯 Java 8 中,它看起来像这样:

    optA
      .map(a -> finder.findB(a.bid)
        .map(b -> b.bid)
        .orElse("ERR_2"))
      .orElse("ERR_1");
    

    【讨论】:

    • 这是令人担忧的;你不能返回一个不同的类型来指定可选的内容。这意味着您不能使用orElse 来返回"ERR_2",因为它是String
    • @Makoto 检查代码示例。 ids 也是字符串。
    • 这很公平。但是,这仍然让我担心;一种是使用字符串类型的错误,而不是实际的异常。您的答案可能会捕获逐字代码,但它违反了为特定目的使用特定类型的关键原则。
    • @Makoto 我猜这些不是真正的错误代码,只是一些用于更好地说明流程的占位符类型
    【解决方案3】:

    您会希望将其调整到您的代码中,但这是我会使用的一种方法。

    首先,为每个故障点隔离要封装的错误。

    Supplier<? extends RuntimeException> missingAException = IllegalStateException::new;
    Supplier<? extends RuntimeException? missingBException = IllegalStateException::new;
    

    这样做可以让您稍后编写一个 lambda,以便根据需要提供特定的错误消息。

    现在,我们编写可选项。

    Optional<A> optA = finder.find(input);
    Optional<B> optB = finder.findB(optA.orElseThrow(missingAException));
    

    要提取optB,请使用与optA 相同的模式。

    B value = optB.orElseThrow(missingBException);
    

    【讨论】:

    • 除了我想避免的例外情况,这样做要容易得多。
    • @Opal:您是否想为结果提供一个哨兵值?否则,您将返回使用 if 语句测试是否存在。
    • 你到底是什么意思?我想我没有这样的价值。
    • @Opal:我问你的是,如果finder.find 没有任何东西返回,你是否有一个可以返回的值,而不是null。您提到"ERR_1" 和其他字符串作为字符串,但这些在我看来是例外(因为您不能用Optional 返回String)。
    • @Makoto 而不是 Exceptions,如果我们已经在使用 Javaslang,最好使用 Try
    猜你喜欢
    • 2023-03-15
    • 2016-07-09
    • 2016-02-08
    • 1970-01-01
    • 2012-06-26
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多