使用Java8的Optional 以功能方式更新源的默认值

时间:2023-02-06 19:04:10

This is probably more a question about functional programming than about Java 8 specifically, but it's what I'm using right now.

这可能是关于函数式编程的问题,而不是Java 8的问题,但它正是我现在正在使用的。

I have a source object (could represent a repository, or a session..., doesn't matter here) that has a method retrieveSomething() that returns an Optional<SomethingA>. I have a method somewhere that returns a Something, by calling retrieveSomething() and providing a default value in case the optional was empty, as follows:

我有一个源对象(可能代表一个存储库,或者一个会话......,这里没关系),它有一个返回Optional 的方法retrieveSomething()。我有一个方法返回一个Something,通过调用retrieveSomething()并在可选项为空的情况下提供默认值,如下所示:

return source.retrieveSomething()
             .orElseGet(() -> provideDefaultValue());

Now I want to modify this code so that in case the source didn't contain any value yet (so the optional was empty), the source is updated with the provided default value.

现在我想修改此代码,以便在源不包含任何值的情况下(因此可选项为空),使用提供的默认值更新源代码。

Of course, I could easily do that inside a lambda expression code block:

当然,我可以在lambda表达式代码块中轻松完成:

return source.retrieveSomething()
             .orElseGet(() -> {
                     Something sth = provideDefaultValue()
                     source.putSomething(sth);
                     return sth;
                 });

But if I understand correctly, I'm not supposed to use functions that cause side effects. So what's the "correct" (functional) way to do this, keeping the benefit of using Optional (in real code I'm actually also performing a map operation on it, but that's irrelevant here) ?

但如果我理解正确,我不应该使用导致副作用的功能。那么“正确”(功能)方式是什么,保持使用Optional的好处(在实际代码中我实际上也在对它执行地图操作,但这在这里无关紧要)?

3 个解决方案

#1


5  

You could follow the way Java does this with Map.computeIfAbsent()

您可以按照Java使用Map.computeIfAbsent()的方式执行此操作

which takes a second parameter which is a function on how to compute and insert the record:

它接受第二个参数,它是如何计算和插入记录的函数:

So your code would become:

所以你的代码将成为:

Something something = source.computeIfAbsent(sth, (k)->provideDefaultValue());

An advantage of using a lambda to compute the default instead of just passing it in, is the lambda will only be evaluated if it needs to be so if computing the default is expensive, you only have to pay it when you need it.

使用lambda来计算默认值而不是仅仅传入它的优点是,只有在需要时才计算lambda,如果计算默认值很昂贵,则只需在需要时付费即可。

#2


0  

From a conceptual standpoint, you use the optional pattern to deal with the absence of a return value. This means, if your source instance doesn't contain a value for you to use, you have the choice of providing a default value to use in its place.

从概念的角度来看,您使用可选模式来处理缺少返回值。这意味着,如果您的源实例不包含您要使用的值,则可以选择提供要在其位置使用的默认值。

It is not advised to modify the Optional directly to provide its value; that instance may be temporal and will differ on subsequent calls to retrieve it.

建议不要直接修改Optional来提供其价值;该实例可能是暂时的,并且在后续调用它时会有所不同。

Since a function call truly governs what's returned by that Optional, the only approach you have if you truly want to go down this route is to modify how that value is computed. This really should be done from within the function providing the Optional, but it could be done outside of it if necessary.

由于函数调用确实可以控制该Optional返回的内容,因此如果您真正想要沿着这条路线前进,那么唯一的方法是修改该值的计算方式。这确实应该在提供Optional的函数内完成,但如果需要可以在它之外完成。

Since there's not enough code structure here to truly write up some example, I will describe the steps:

由于这里没有足够的代码结构来真正写出一些例子,我将描述这些步骤:

  • Outside of the method providing the Optional, you write the same closure as you did before, with the side effect of adjusting the value used to compute the original Optional. This is likely a field of some sort.

    在提供Optional的方法之外,您可以像以前一样编写相同的闭包,其副作用是调整用于计算原始Optional的值。这可能是某种领域。

  • Inside of the method providing the Optional, you ensure that you don't expose provideDefaultValue anywhere else (since they won't need it), and use a boolean conditional before you package the Optional.

    在提供Optional的方法内部,您确保不在其他任何地方公开provideDefaultValue(因为它们不需要它),并在打包Optional之前使用布尔条件。

    return value == null ? Optional.of(provideDefaultValue()) : Optional.of(value);
    

    ...but that really defeats the purpose of the Optional, as you're still doing a null check.

    ...但这确实违背了Optional的目的,因为你仍在进行空检查。

    A slightly better approach to the above would be to compute value in such a way that it was either itself or the default value, and return the Optional of that...

    稍微好一点的方法是以这样的方式计算值,使其本身或默认值,并返回可选的......

    value = computeValue();
    if(null == value) {
        value = provideDefaultValue();
    }
    return Optional.of(value);
    

    ...but again, seriously defeating the purpose of Optional, as you're doing null checks.

    ......但是,再次,严重地击败了Optional的目的,因为你正在进行空检查。

#3


0  

An answer I came up with myself, which I'm not entirely satisfied with, but may serve as an example of what I'm looking for:

我自己提出的答案,我并不完全满意,但可以作为我正在寻找的一个例子:

I could implement something similar to a Pair<V1,V2> class and then map the Optional<Something> to a Pair<Something, Boolean> where the Boolean value would indicate whether or not the value was a generated default:

我可以实现类似于Pair 类的东西,然后将Optional 映射到Pair ,其中布尔值将指示该值是否是生成的默认值: ,boolean> ,v2>

Pair<Something, Boolean> somethingMaybeDefault = 
    source.retrieveSomething()
          .map(sth -> new Pair<Something, Boolean>(sth, false))
          .orElseGet(() -> new Pair<Something, Boolean>(provideDefaultValue(), true));

Then I'd update in case the boolean is false:

然后我会更新以防布尔值为false:

if (somethingMaybeDefault.value2()) {
    source.putSomething(somethingMaybeDefault.value1());
}

And finally return the new value:

最后返回新值:

return somethingMaybeDefault.value1();

Of course, this uses imperative style for the update, but at least the functions remain pure. I'm not sure this is the best possible answer though.

当然,这使用命令式样式进行更新,但至少功能仍然是纯粹的。我不确定这是最好的答案。

#1


5  

You could follow the way Java does this with Map.computeIfAbsent()

您可以按照Java使用Map.computeIfAbsent()的方式执行此操作

which takes a second parameter which is a function on how to compute and insert the record:

它接受第二个参数,它是如何计算和插入记录的函数:

So your code would become:

所以你的代码将成为:

Something something = source.computeIfAbsent(sth, (k)->provideDefaultValue());

An advantage of using a lambda to compute the default instead of just passing it in, is the lambda will only be evaluated if it needs to be so if computing the default is expensive, you only have to pay it when you need it.

使用lambda来计算默认值而不是仅仅传入它的优点是,只有在需要时才计算lambda,如果计算默认值很昂贵,则只需在需要时付费即可。

#2


0  

From a conceptual standpoint, you use the optional pattern to deal with the absence of a return value. This means, if your source instance doesn't contain a value for you to use, you have the choice of providing a default value to use in its place.

从概念的角度来看,您使用可选模式来处理缺少返回值。这意味着,如果您的源实例不包含您要使用的值,则可以选择提供要在其位置使用的默认值。

It is not advised to modify the Optional directly to provide its value; that instance may be temporal and will differ on subsequent calls to retrieve it.

建议不要直接修改Optional来提供其价值;该实例可能是暂时的,并且在后续调用它时会有所不同。

Since a function call truly governs what's returned by that Optional, the only approach you have if you truly want to go down this route is to modify how that value is computed. This really should be done from within the function providing the Optional, but it could be done outside of it if necessary.

由于函数调用确实可以控制该Optional返回的内容,因此如果您真正想要沿着这条路线前进,那么唯一的方法是修改该值的计算方式。这确实应该在提供Optional的函数内完成,但如果需要可以在它之外完成。

Since there's not enough code structure here to truly write up some example, I will describe the steps:

由于这里没有足够的代码结构来真正写出一些例子,我将描述这些步骤:

  • Outside of the method providing the Optional, you write the same closure as you did before, with the side effect of adjusting the value used to compute the original Optional. This is likely a field of some sort.

    在提供Optional的方法之外,您可以像以前一样编写相同的闭包,其副作用是调整用于计算原始Optional的值。这可能是某种领域。

  • Inside of the method providing the Optional, you ensure that you don't expose provideDefaultValue anywhere else (since they won't need it), and use a boolean conditional before you package the Optional.

    在提供Optional的方法内部,您确保不在其他任何地方公开provideDefaultValue(因为它们不需要它),并在打包Optional之前使用布尔条件。

    return value == null ? Optional.of(provideDefaultValue()) : Optional.of(value);
    

    ...but that really defeats the purpose of the Optional, as you're still doing a null check.

    ...但这确实违背了Optional的目的,因为你仍在进行空检查。

    A slightly better approach to the above would be to compute value in such a way that it was either itself or the default value, and return the Optional of that...

    稍微好一点的方法是以这样的方式计算值,使其本身或默认值,并返回可选的......

    value = computeValue();
    if(null == value) {
        value = provideDefaultValue();
    }
    return Optional.of(value);
    

    ...but again, seriously defeating the purpose of Optional, as you're doing null checks.

    ......但是,再次,严重地击败了Optional的目的,因为你正在进行空检查。

#3


0  

An answer I came up with myself, which I'm not entirely satisfied with, but may serve as an example of what I'm looking for:

我自己提出的答案,我并不完全满意,但可以作为我正在寻找的一个例子:

I could implement something similar to a Pair<V1,V2> class and then map the Optional<Something> to a Pair<Something, Boolean> where the Boolean value would indicate whether or not the value was a generated default:

我可以实现类似于Pair 类的东西,然后将Optional 映射到Pair ,其中布尔值将指示该值是否是生成的默认值: ,boolean> ,v2>

Pair<Something, Boolean> somethingMaybeDefault = 
    source.retrieveSomething()
          .map(sth -> new Pair<Something, Boolean>(sth, false))
          .orElseGet(() -> new Pair<Something, Boolean>(provideDefaultValue(), true));

Then I'd update in case the boolean is false:

然后我会更新以防布尔值为false:

if (somethingMaybeDefault.value2()) {
    source.putSomething(somethingMaybeDefault.value1());
}

And finally return the new value:

最后返回新值:

return somethingMaybeDefault.value1();

Of course, this uses imperative style for the update, but at least the functions remain pure. I'm not sure this is the best possible answer though.

当然,这使用命令式样式进行更新,但至少功能仍然是纯粹的。我不确定这是最好的答案。