Java 8中Elvis运算符的空值检查?

18
问题: 是否有Java的未来版本中计划实现Elvis操作符?或者是否有任何库将其引入Java?据我所知,它曾被提议用于Java SE 7,但并未成为该版本的一部分。Java 8允许使用该操作符。http://www.oracle.com/technetwork/articles/java/java8-optional-2175753.html
String name = computer.flatMap(Computer::getSoundcard)
                          .flatMap(Soundcard::getUSB)
                          .map(USB::getVersion)
                          .orElse("UNKNOWN");

但我觉得这有点过于夸张了。如果有人能给我指出任何一个项目/库,可以将Groovy-like/C#-like的语法引入Java的空值检查中,我将十分感激。

编辑:我的意思是Elvis运算符,就像这样:

String version = computer?.getSoundcard()?.getUSB()?.getVersion();

或者类似的


4
Optional是Java 8的一部分,但我不了解Elvis操作符。 - ooxi
5
嗯,“?. ”不是Groovy所称的Elvis运算符。正确的写法应该是“?:”。 - mabi
2
“Safe navigation”运算符。Elvis运算符是一个简化的三元运算符,如果被操作的项目为空或为falsey,则结果要么是该项目本身,否则是替代方案。 - Dave Newton
谢谢您澄清,那我应该改变问题的标题吗? - user73362
Elvis运算符在这里进行了讨论:http://mail.openjdk.java.net/pipermail/coin-dev/2009-March/000047.html - lbalazscs
2个回答

18

不。 目前没有重新考虑在Java中使用空安全运算符的计划。


1
自2014年10月23日至今(2016年6月),是否有关于现在实施它的任何想法? - Srujan Barai
2
@SrujanBarai 不是。C#语法再次胜出。 - Zaaier
1
那么。你简直无法得到比这个答案更权威的回复了。 - scottb
1
作为一名同时精通Groovy和Java的开发者,我认为Java应该提供这样一个重要的功能,因为几乎有10%的代码是用来检查null的,无论是在if条件语句中还是在try...catch块中。但是在Groovy中,只需要在同一行中使用?(安全导航运算符),就可以完成操作,而不需要任何额外的代码。 - Nitin Dhomse
即使在Groovy中,闭包的真相我确实错过了Groovy的魔力,即使您现在将Java升级到17版本。 - Nitin Dhomse

3
很久以前,有一个函数。
public static Foo getFoo(Bar bar) {
  return bar.getFoo();
}

人们对于如果bar为空时应该发生什么意见不一致。

首先,会有人声称违反函数意图应该受到检查异常的惩罚。

public static Foo getFoo(Bar bar) throws FooNotFoundException {
  if (bar == null) throw new FooNotFoundException();
  return bar.getFoo();
}

函数的调用者将被迫考虑这种情况。它必须捕获异常或重新抛出异常。这样做是如此强制性的,以至于它会令人烦恼,不久之后人们会争论这不应该是一个受检异常,而是一个运行时异常,以使其 less forceful。
public static Foo getFoo(Bar bar) {
  if (bar == null) throw new FooNotFoundException();
  return bar.getFoo();
}

有人会认为抛出异常没有意义,因为Java本身已经会抛出一个异常:空指针异常

那些担心意外异常的人会使他们的代码更加健壮,并在函数开头添加null检查,这将简单地返回null

public static Foo getFoo(Bar bar) {
  if (bar == null) return null;
  return bar.getFoo();
}

不久之后,人们会争论是否可以返回空列表,或者这些空列表是否应该被视为null值。 "当然,你不想在每次迭代之前检查可空性,对吧?" 另一方会反驳。很快,他们将创建各种构造来完全避免空值问题

public static Foo getFoo(Bar bar) {
  if (bar == null) return Foo.Empty;
  return bar.getFoo();
}

每种方法都会产生广泛的后果。这些后果将使得不同方法的结合变得困难。这些后果将导致编码规则,其中每个单独的规则都与下一个规则相连。它们相互支持的方式会给人留下一种印象,即每个规则都是无可辩驳的。最终,编码规则会变成像宗教一样,其推理只在完整规则集的范围内才有意义。
根据您选择的规则集,使用某些框架可能会遇到困难。 最终,带有未经检查的异常的空列表宗教成为了主导。 这种宗教可以概括如下:
  • 应避免返回null值。
  • 如果列表为空,则按原样返回。
  • 如果迭代列表,则无需检查null。
  • 方法不应抛出已检查异常
  • 应将已检查异常包装在运行时异常中。
  • 字符串不应该为空,而应该是""

显然,这种宗教变得如此强大,以至于它设法影响了框架和语言规范。

  • 针对空列表的编译器优化
  • Optional
  • 值类型

一些外部库和编辑器实际上会尝试通过提供注释(@Null@NotNull)来重新联合不同的团队。IDE将为您标记所有违规行为。这是一个简单但有效的解决方案。然而,JDK从未包含自己的@Null@NotNull,而是每个库都必须提供自己的注释。

考虑到所有这些,现在很可能永远不会在Java中出现Elvis操作符了。如果想在Java中编码,最好忘记null

或者用Tony Hoare(null的发明者)的话说:

我把它称为我的十亿美元错误。这是1965年发明null引用的时候。那时,我正在设计一个面向对象语言(ALGOL W)中引用的第一个全面类型系统。我的目标是确保所有引用使用都绝对安全,并由编译器自动执行检查。但是,我无法抗拒放置null引用的诱惑,因为它非常容易实现。这导致了无数的错误,漏洞和系统崩溃,在过去的四十年中可能造成了数十亿美元的痛苦和损失。

个人认为,鉴于每种体面的编程语言都有null,这绝对没有任何意义。一些甚至有多个null值来表示不同类型的nullability。毕竟,甚至在数学中也有未定义的值

无论如何,如果您没有Elvis操作符,仍然可以

Foo foo = bar == null ? null : bar.getFoo();

这正好符合 Java 的精神。毕竟,在 2021 年,Java 是一种非常明确的语言。


Foo foo = bar == null ? null : bar.getFoo(); is more or less ok, but the example from the question is not that nice anymore, and a real pain to write over and over again: Version version = computer == null ? null : computer.getSoundcard() == null ? null computer.getSoundcard().getUSB() == null ? null computer.getSoundcard().getUSB().getVersion() - Tom Carmi
此外,我认为问题更多地涉及嵌套对象的复杂对象,而您所说的更多是列表和字符串的可空性。 - Tom Carmi
@TomCarmi,通常你可以用三行代码来实现这个例子。像这样: SoundCard soundCard = computer == null ? null : computer.getSoundCard(); USB usb = soundCard == null ? null : soundCard.getUSB(); Version version = usb == null ? null : usb.getVersion();。每一行都解决了一个步骤。但是,我同意这太啰嗦了。 - bvdb
在我的理解中,这个问题是关于“未来是否会有一个 Elvis 操作符”的。你可能想要阅读 Brian Goetz 的非常正确和简短的答案下面的评论部分。你会发现人们通常不会接受“不”的回答。因此,我试图解释 Java 中 null 背后漫长而繁琐的文化演变。尽管我不同意它选择的路径。 - bvdb

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