java.util.Objects和Optional,哪个更好?

19

java.util.Objects 类在 Java 9 中新增了一些方法。

Objects#requireNonNullElse 方法和 Objects#requireNonNullElseGet() 方法分别实现了以下功能:

如果第一个参数非空,则返回该参数;否则返回第二个非空参数或 supplier.get() 的非空值。

jshell> String nullStr = null;
nullStr ==> null

jshell> Objects.requireNonNullElse(nullStr,"lorem ipsum");
$13 ==> "lorem ipsum"

jshell> Objects.requireNonNullElseGet(nullStr,() -> "lorem ipsum");
$14 ==> "lorem ipsum"

但是这个新功能与Optional类中已存在的Optional#orElseOptional#orElseGet有重叠。

jshell> Optional.ofNullable(nullStr).orElse("lorem ipsum");
$17 ==> "lorem ipsum"

jshell> Optional.ofNullable(nullStr).orElseGet(() -> "lorem ipsum");
$18 ==> "lorem ipsum"

Objects中的新方法与相应的Optional方法之间唯一的区别是,第二个参数或者供应商的值必须为非空,否则Objects会抛出NPE异常:

jshell> Objects.requireNonNullElseGet(nullStr,() -> null);
|  java.lang.NullPointerException thrown: supplier.get()
|        at Objects.requireNonNull (Objects.java:246)
|        at Objects.requireNonNullElseGet (Objects.java:321)
|        at (#15:1)

jshell> Objects.requireNonNullElse(nullStr,null);
|  java.lang.NullPointerException thrown: defaultObj
|        at Objects.requireNonNull (Objects.java:246)
|        at Objects.requireNonNullElse (Objects.java:301)
|        at (#16:1)

对比 Optional

jshell> Optional.ofNullable(nullStr).orElse(null);
$19 ==> null

jshell> Optional.ofNullable(nullStr).orElseGet(() -> null);
$20 ==> null
  • JDK开发人员为什么没有更新Optional类中现有的方法?
  • 他们为什么没有引入一个新方法(如果第二个参数为null,将抛出NPE)到Optional类中?
  • 现在应该使用Optional还是Objects?
  • 新方法是否使得Objects比Optional更可取,因为它们会立即抛出NPE而不是像Optional那样在代码的其他地方稍后抛出?

如果我有一个遗留代码,就像这样:

String str = null; 
String result = str == null ? "other string" : str;

这只是一个方法内部的简单检查。我想使用最新的语言功能进行重构。现在考虑到 Optional.orElseObjects.requireNonNullElse 之间的区别,哪个更可取?

result = Optional.ofNullable(str).orElse("other string");
或者
result = Objects.requireNonNullOrElse(str,"other string);
5个回答

23

回答你的问题"哪个更好?"的最短答案是开发者们永远钟爱的 "it depends",因为Objects::requireNonNullElseOptional对于不同的使用情况有不同的覆盖。

两种选择

在回答你的问题之前,我想先介绍一下这两种选择的背景。

Objects::requireNonNullElse

Objects::requireNonNull 确保调用的结果永远不会为 null(因此得名)。通常用于简洁地验证构造函数或方法参数,并允许读者通过一眼查看来验证分配给返回值的变量不能为 null。

所以,让 Objects::requireNonNullElse 突然允许 null 不仅很奇怪,而且几乎没有意义,因为:

// if requireNonNullGet would allow null as second argument,
// the following is true for all x (including null)
Objects.requireNonNullElse(x, null) == x

你可能会认为requireNonNullElseGet与众不同,因为它可能调用一个函数,根据某些状态可能返回null或非null。这是正确的,我认为这已经被考虑过了,但如果在三种情况中的一种实际上允许调用的最终结果为null,即使名称为 required non null ,那么 requireNonNull ... API将会非常奇怪。

Optional

Optional 被设计为在返回null很可能导致NPE的情况下作为返回参数(例如对于Stream的终止操作,这是它首次使用的场景)。尽管有些开发人员更倾向于在更多的情况下使用它(请参考Stephen Colebourne的务实方法我的严格方法),但没有人真正建议像您演示的那样使用它。

Optional.ofNullable(nullStr).orElse(null);
Optional.ofNullable(nullStr).orElseGet(() -> null);
Optional是一种在类型系统中表示某些内容可能不存在的方式,而不是替代if-null检查的选择。从这个意义上讲,orElseorElseGet是从Optional回到可空类型世界的后门,有时如果某些内容不存在,则使用null是合理的,因此对它们接受null作为参数(或供应商的结果)也是有意义的。
现在我们已经有了回答您问题所需的信息:
为什么JDK开发人员没有更新Optional类中现有的方法? 从概念上讲,这将违反Optional应该用于的目的。但正如其他人提到的,这将是一种向后不兼容的更改,因为对orElse(null)的调用将突然引发异常。
为什么他们没有在Optional类中引入新方法(如果第二个参数为null则引发NPE)? 只有在可以预期显着改进现有代码的情况下才会扩展API。我在这里看不到。在许多情况下,orElse获取一个调用者专门为选项为空而创建的参数-很少需要进行额外的检查以验证它不是null。如果确实需要,请调用orElse(requireNonNull(x))。
现在Optional和Objects应该使用哪个? 如果您有一个变量(无论是局部变量,参数还是字段),并且您想确保它不为空,请使用Objects。如果要返回可能为null的内容,请考虑将其包装在Optional中。对于创建Optional(而不是从调用中获取一个)并在同一链的末尾解包它的代码要持怀疑态度。
由于新方法会立即引发NPE,而不像Optional那样延迟到代码的其他地方,因此是否使Objects更可取? 正如现在清楚的那样,它们涵盖不同的用例。但是请允许我回答“并且不像Optional一样稍后在代码的其他地方”:无论做什么,请务必在代码中检查所需的可空属性(可以为null或不可为null)。不要返回您认为不能为null的内容,但实际上却是因为您没有进行检查。如果发生这种情况,这不是Optional的错。
如果我有一个类似于遗留代码的东西:
String str = null; 
String result = str == null ? "other string" : str;
一定要使用Objects.requireNonNullOrElse(str,"其他字符串");,考虑使用静态导入使其更易读。

1
我想我使用Optional的方式不正确。我曾认为它们可以用作if-null-check的替代方案。现在我将重新审查我的方法。非常感谢。 - Anton Balaniuc
1
这就是SO存在的目的。:) 如果你的问题得到了完全解决,请记得接受其中一个答案。 - Nicolai Parlog

4
为什么JDK开发人员没有更新Optional类中的现有方法?
因为这会引入一种破坏性变化,会破坏许多现有程序,并且该方法应该允许在需要时获取null。
为什么他们没有向Optional类中引入一个新方法(如果第二个参数为空,则将抛出NPE)?
可能是因为那样会使API更加复杂和臃肿,而没有显著的优势。如果您想确保代码不会意外返回null,可以使用requireNonNull包装结果。
现在应该使用Optional还是Objects?
如果您需要从方法返回的可选项中提取值,请使用Optional的方法。如果您想确保方法的参数应该不为null的前提条件得到遵守,请使用Object.requireXxx。 JDK设计人员从未主张仅用Optional包装值并检查null。 Optional用于返回值。
由于新方法会立即抛出NPE而不是稍后在代码的其他位置中,这是否使Objects更受欢迎?
请参见先前提到的观点:您不使用这些方法来执行相同的操作。

3
重点是:这两种方法的签名明显不同:
public static <T> T requireNonNullElse(T obj, T defaultObj)

对比。

public static <T> T requireNonNullElseGet(T obj, Supplier<? extends T> supplier)

第二个方法的javadoc如下:

如果第一个参数非空,返回第一个参数;否则返回 supplier.get() 的非空值。

换句话说:它使用你在此提供的supplier。所以,答案是:你使用第二个版本来处理需要使用 supplier 的情况;否则,你可以选择 "更简单" 的那个方法,它接受 "不太复杂" 的参数。

这么做的理由是:当你在两种选项之间做出决定时,你更喜欢使用更容易/"不会让读者感到惊讶"的那个选项。换句话说:为什么要提供一个supplier,当你可以不用它呢?

关于Optional的使用 - 请记住,它们的主要目标是用于返回类型;而不是作为方法参数(有关详细信息,请参见此处)。

然后:几乎总是禁止在“交付给现场”的类中更新现有方法。你绝对不想改变已经公开并被你的客户使用的东西的语义,这样做会导致特定语义的假设。


是的,但是 Optional#orElseGetObjects.requireNonNullElseGet 都接受供应商。唯一的区别是,如果 supplier.get() 返回 null,第二个方法将抛出 NPE。 - Anton Balaniuc
1
那个方面在最后一段中有涉及 - 给我们 Java 的人认为将 Optional 用作方法参数不是一个好的实践; 那么他们为什么会发布一个正式的方法签名,恰好做到了这一点呢?! - GhostCat
我更多地考虑以下情况。假设在一个方法内有一个检查:String str = null; String result = str == null ? "other string" : str; 它不是一个方法参数,只是一个检查。现在我想使用最新的语言特性对其进行重构。考虑ObjectsOptional之间的差异,那么哪个更可取? result = Optional.ofNullable(str).orElse("other string"); 还是 result = Objects.requireNonNullOrElse(str,"other string); - Anton Balaniuc

1

问:为什么JDK开发人员没有更新Optional类中的现有方法?

答:因为Optional类旨在避免NPE。

问:为什么他们没有在Optional类中引入一个新方法(如果第二个参数为空,则会抛出NPE)?

答:同样的答案。

问:现在应该使用Optional还是Objects?

答:两者都可以。对于简单的“非空”检查,使用Objects,对于链式操作,如mapflatMapfilter,使用Optional

问:新方法是否使Objects比Optional更可取,因为它们会立即抛出NPE,而不像Optional那样在代码的其他地方后期才会抛出?

答:这取决于情况。如果您已经有Optional作为某个方法的返回值,则最好使用Optional。


0

所以主要的问题是,如果你喜欢使用if语句来检查是否为空。如果你有一个可以接受可能为空值的参数的方法,那么使用Objects会更有意义。唯一合理使用Optional的地方是验证返回可选结果的方法的调用。

开发人员希望使用更多功能性的方式来检查空值,因此他们使用构造Optional.ofNullable来实现这个目的,但这不是一个好的做法,因为它会创建垃圾。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接