如何避免在 Java 中检查空值?

How do I avoid checking for nulls in Java?

提问人: 提问时间:11/7/2008 最后编辑:38 revs, 30 users 11%Goran Martinic 更新时间:11/8/2023 访问量:1427699

问:

我用来避免 NullPointerException。有其他选择吗?x != null

if (x != null) {
    // ...
}
java nullpointerException null

评论

142赞 Tom Hawtin - tackline 8/2/2010
@Shervin 鼓励 null 值会使代码更难理解且不太可靠。
83赞 ianpojman 6/9/2012
不使用 null 优于此处的大多数其他建议。引发异常,不返回或允许 null。顺便说一句 - “assert”关键字是无用的,因为它默认处于禁用状态。使用始终启用的故障机制
4赞 Andrii Polunin 4/4/2016
在高级代码中应避免 Null。发明空引用的托尼·霍尔(Tony Hoare)称其为“十亿美元的错误”。看看这里的一些想法。
3赞 Robert R Evans 6/24/2016
似乎在 Java 8 中:static Objects.isNull(Object o) docs.oracle.com/javase/8/docs/api/java/util/Objects.html
7赞 Jack 6/29/2021
null 最常见的误用是人们返回 null 而不是空集合。每次都让我发疯。停止使用空值,您将生活在一个更美好的世界。此外,用关键字传播您的代码,您将生活在一个更美好的世界。final

答:

83赞 Jim Nelson #1

与其说是 Null 对象模式(有其用途),不如考虑 null 对象是 bug 的情况。

引发异常时,请检查堆栈跟踪并解决 bug。

评论

19赞 Thorbjørn Ravn Andersen 10/31/2009
问题是通常你会失去上下文,因为 NullPointerException 不指示哪个变量为 null,并且你可能有几个“.”-生产线上的操作。使用 “if (foo == null) throw new RuntimeException(”foo == null“)” 可以让你明确说明出了什么问题,让你的堆栈跟踪对那些必须修复它的人有更多的价值。
1赞 cthulhu 1/18/2011
对于 Andersen - 我希望 Java 的异常系统能够包含正在处理的变量的名称,这样 NullPointerExceptions 不仅会指示发生异常的行,还会指示变量名称。这在未混淆的软件中应该可以正常工作。
3赞 RustyTheBoyRobot 4/17/2013
我有一位教授反对方法调用链接。他的理论是,你应该警惕长度超过 2 个方法的调用链。我不知道这是否是一个硬性规则,但它绝对消除了 NPE 堆栈跟踪的大部分问题。
0赞 Haakon Løtveit 2/1/2023
@RustyTheBoyRobot 您可以将链上的每个调用放在自己的线路上。这样一来,你仍然可以用链子锁住,直到奶牛回家,但你确切地知道哪里出了问题。
104赞 2 revs, 2 users 100%pgras #2
  • 如果您认为某个对象不应为 null(或者它是一个 bug),请使用断言。
  • 如果你的方法不接受 null 参数,请在 javadoc 中说出来并使用断言。

仅当您要处理对象可能为 null 的情况时,才必须检查对象 != null...

有人建议在 Java7 中添加新的注解来帮助处理 null / notnull 参数: http://tech.puredanger.com/java7/#jsr308

评论

4赞 phil294 2/26/2017
不可以,不要在生产代码中使用断言
84赞 4 revs, 4 users 50%Johannes Schaub - litb #3

有时,您可以使用一些方法对其参数进行操作,这些参数定义了对称操作:

a.f(b); <-> b.f(a);

如果你知道 b 永远不能为空,你可以交换它。它对等于最有用: 而不是最好做.foo.equals("bar");"bar".equals(foo);

评论

2赞 Supericy 6/14/2013
但是,您必须假设(可以是任何方法)将正确处理 null。实际上,这一切所做的只是将责任转嫁给其他人(或其他方法)。equals
5赞 Angelo Fuchs 7/30/2013
@Supericy 基本上是可以的,但是(或任何方法)无论如何都必须检查。或者明确说明它没有。equalsnull
134赞 3 revs, 3 users 68%javamonkey79 #4

根据您正在检查的对象类型,您可以使用apache commons中的一些类,例如:apache commons langapache commons collections

例:

String foo;
...
if( StringUtils.isBlank( foo ) ) {
   ///do something
}

或者(取决于您需要检查的内容):

String foo;
...
if( StringUtils.isEmpty( foo ) ) {
   ///do something
}

StringUtils 类只是众多类之一;在 Commons 中有很多很好的类可以执行 null 安全操作。

下面是一个示例,说明当您包含 apache 库(commons-lang-2.4.jar) 时如何在 JAVA 中使用空值

public DOCUMENT read(String xml, ValidationEventHandler validationEventHandler) {
    Validate.notNull(validationEventHandler,"ValidationHandler not Injected");
    return read(new StringReader(xml), true, validationEventHandler);
}

如果你使用的是Spring,Spring的包中也有相同的功能,参见library(spring-2.4.6.jar)

如何从spring(org.springframework.util.Assert)使用这个静态类的示例

Assert.notNull(validationEventHandler,"ValidationHandler not Injected");

评论

5赞 monojohnny 1/14/2010
您也可以使用Apache Commons中更通用的版本,这在检查我找到的参数的方法开始时非常有用。Validate.notNull( 对象, “对象不能为空”);commons.apache.org/lang/apidocs/org/apache/commons/lang/......
0赞 TomasMolina 12/10/2017
Validate 将 Assert 语句@monojohnny用于?。我问,因为 Assert 可能会在 JVM 上激活/停用,建议不要在生产中使用。
0赞 monojohnny 1/6/2018
不要这么认为 - 我相信如果验证失败,它只会抛出 RuntimeException
351赞 2 revs, 2 users 90%myplacedk #5

如果不允许 null 值

如果方法是在外部调用的,请从如下所示的内容开始:

public void method(Object object) {
  if (object == null) {
    throw new IllegalArgumentException("...");
  }

然后,在该方法的其余部分,您将知道它不是 null。object

如果它是一个内部方法(不是 API 的一部分),只需记录它不能为 null,仅此而已。

例:

public String getFirst3Chars(String text) {
  return text.subString(0, 3);
}

但是,如果你的方法只是传递值,而下一个方法传递它,等等,它可能会有问题。在这种情况下,您可能需要检查上述参数。

如果允许 null

这真的要看情况。如果发现我经常做这样的事情:

if (object == null) {
  // something
} else {
  // something else
}

所以我分支,做两件完全不同的事情。没有丑陋的代码片段,因为我真的需要根据数据做两件不同的事情。例如,我应该处理输入,还是应该计算一个好的默认值?


实际上,我很少使用成语“”。if (object != null && ...

如果您举例说明您通常在哪里使用该成语,那么举例可能会更容易。

评论

101赞 Axel 8/10/2011
抛出 IllegalArgumentException 有什么意义?我认为 NullPointerException 会更清楚,如果您自己不进行 null 检查,也会抛出这种情况。我要么使用断言,要么什么都不用。
24赞 myplacedk 8/15/2011
除 null 之外的所有其他值都不太可能被接受。你可以有 IllegalArgumentException、OutOfRageException 等。有时这是有道理的。其他时候,您最终会创建许多不增加任何值的异常类,然后您只需使用 IllegalArgumentException。对 null-input 有一个例外,而对其他所有内容设置另一个例外是没有意义的。
8赞 fgb 4/8/2013
安全漏洞?JDK 中充满了这样的代码。如果您不希望用户看到堆栈跟踪,则只需禁用它们即可。没有人暗示该行为没有被记录在案。MySQL是用C语言编写的,其中取消引用空指针是未定义的行为,与抛出异常完全不同。
9赞 Thorbjørn Ravn Andersen 4/21/2013
throw new IllegalArgumentException("object==null")
8赞 luis.espinal 8/13/2016
1/2 我必须同意,对于这些情况,a 比普通的香草更好。我认为 NPE 表明在代码中的某个地方,某些东西不安全地取消了对恰好为 null 的表达式的引用(假设满足所有已知和声明的前提条件和不变量)。非法参数异常 OTH 告诉我未满足众所周知的前提条件或不变性。IllegalArgumentExceptionNPE
39赞 2 revsxtofl #6

提出这个问题表明您可能对错误处理策略感兴趣。如何以及在何处处理错误是一个普遍存在的架构问题。有几种方法可以做到这一点。

我最喜欢的:允许异常涟漪 - 在“主循环”或其他具有适当职责的函数中捕获它们。检查错误情况并妥善处理它们可以被视为一项专门的责任。

当然,也要看看面向方面的编程 - 他们有巧妙的方法可以插入到你的字节码中。if( o == null ) handleNull()

6赞 2 revs, 2 users 73%Stuart Axon #7

无论您在何处传递数组或 Vector,请将它们初始化为空,而不是 null。- 这样你就可以避免大量的空值检查,一切都很好:)

public class NonNullThing {

   Vector vectorField = new Vector();

   int[] arrayField = new int[0];

   public NonNullThing() {

      // etc

   }

}
2846赞 14 revs, 11 users 74%cletus #8

在我看来,这听起来像是一个相当普遍的问题,初级到中级开发人员在某个时候往往会面临:他们要么不知道,要么不信任他们参与的合同,并且防御性地过度检查空值。此外,在编写自己的代码时,它们倾向于依赖返回 null 来指示某些内容,因此需要调用方检查 null。

换句话说,有两种情况下会出现 null 检查:

  1. 其中 null 是合同方面的有效响应;和

  2. 它不是有效的响应。

(2)容易。从 Java 1.7 开始,您可以使用 Objects.requireNonNull(foo)。(如果您坚持使用以前的版本,那么断言离子可能是一个不错的选择。

此方法的“正确”用法如下所示。该方法返回传递给它的对象,如果对象为 null,则抛出 。这意味着返回的值始终为非 null。该方法主要用于验证参数。NullPointerException

public Foo(Bar bar) {
    this.bar = Objects.requireNonNull(bar);
}

它也可以像离子一样使用,因为如果对象为 null,它会引发异常。在这两种用途中,都可以添加一条消息,该消息将显示在异常中。下面是像断言一样使用它并提供消息。assert

Objects.requireNonNull(someobject, "if someobject is null then something is wrong");
someobject.doCalc();

通常抛出特定的异常(例如,当值为 null 但不应为 null 时)有利于抛出更一般的异常,例如 。这是 Java 库采用的方法;当不允许参数为 null 时,优先。NullPointerExceptionAssertionErrorNullPointerExceptionIllegalArgumentException

(1)有点难。如果你无法控制你正在调用的代码,那么你就被卡住了。如果 null 是有效的响应,则必须检查它。

但是,如果代码由您控制(通常情况如此),那么情况就不同了。避免使用 null 值作为响应。使用返回集合的方法,这很容易:几乎一直返回空集合(或数组)而不是 null。

对于非集合,可能会更难。以此为例:如果您有以下接口:

public interface Action {
  void doSomething();
}

public interface Parser {
  Action findAction(String userInput);
}

Parser 获取原始用户输入并找到要做的事情,也许如果你正在为某些东西实现命令行界面。现在,如果没有适当的操作,您可以使协定返回 null。这导致了您所说的空检查。

另一种解决方案是永不返回 null,而是使用 Null Object 模式

public class MyParser implements Parser {
  private static Action DO_NOTHING = new Action() {
    public void doSomething() { /* do nothing */ }
  };

  public Action findAction(String userInput) {
    // ...
    if ( /* we can't find any actions */ ) {
      return DO_NOTHING;
    }
  }
}

比较:

Parser parser = ParserFactory.getParser();
if (parser == null) {
  // now what?
  // this would be an example of where null isn't (or shouldn't be) a valid response
}
Action action = parser.findAction(someInput);
if (action == null) {
  // do nothing
} else {
  action.doSomething();
}

ParserFactory.getParser().findAction(someInput).doSomething();

这是一个更好的设计,因为它会导致更简洁的代码。

也就是说,也许 findAction() 方法抛出带有有意义的错误消息的 Exception 是完全合适的——尤其是在您依赖用户输入的情况下。findAction 方法抛出 Exception 比调用方法用简单的 NullPointerException 爆炸而没有解释要好得多。

try {
    ParserFactory.getParser().findAction(someInput).doSomething();
} catch(ActionNotFoundException anfe) {
    userConsole.err(anfe.getMessage());
}

或者,如果您认为 try/catch 机制太难看,则默认操作应该向用户提供反馈,而不是什么都不做。

public Action findAction(final String userInput) {
    /* Code to return requested Action if found */
    return new Action() {
        public void doSomething() {
            userConsole.err("Action not found: " + userInput);
        }
    }
}

评论

4赞 Franz D. 12/25/2021
不错的答案,尤其是提到断言。我来自C语言,经常使用它们。至于 Null 对象,它们不是灵丹妙药。我经常花费数小时调试代码,这些代码被证明是正在使用的 Null 对象,当 NPE 可以更清楚地指示问题时,什么都不做(或返回其默认值)。因此,您将不得不执行“空对象检查”而不是空检查,并且没有赢得任何东西,实际上失去了清晰度。如今,如果我不能避免 null,我只需清楚地记录它,仅此而已。
0赞 MiguelMunoz 7/9/2022
写作是这种方法的一个很好的用法,因为你不打算用.这样,在传递无效的 null 对象时,而不是在尝试使用它时,您会得到异常。但是,它通常以这种方式使用:在这里,调用 to 根本不会改变行为,它会以任何一种方式抛出 NPE。最好在保存对象以供以后使用时使用它。在我的第二个示例中,异常很有用。它有助于追踪错误。this.bar = Objects.requireNonNull(bar);this.barObjects.requireNonNull(getThing()).doThingStuff();Objects.requireNonNull()
1赞 Lee White 7/12/2022
但是,我们是否应该建议对控制流使用异常?虽然从技术上讲,这是一个有效的解决方案,但程序员应该记住,异常应该只用于捕获意外情况。在验证用户的输入时,无效的输入理想情况下不应导致引发异常。
211赞 3 revs, 3 users 68%echox #9

仅适用于这种情况——

在调用 equals 方法之前不检查变量是否为 null(下面的字符串比较示例):

if ( foo.equals("bar") ) {
 // ...
}

将导致 if 不存在。NullPointerExceptionfoo

如果你像这样比较你的 s,你可以避免这种情况:String

if ( "bar".equals(foo) ) {
 // ...
}

评论

44赞 Alex Worden 5/24/2011
我同意 - 只有在那种情况下。我无法忍受程序员将其提升到下一个不必要的水平并编写 if (null != myVar)...只是在我看来丑陋,没有任何意义!
21赞 Stef 9/23/2011
这是一个特殊的例子,可能是最常用的,一个普遍的良好做法:如果你知道它,总是这样做。它适用于其他方法,除非您知道合约并且这些方法可以处理参数。<object that you know that is not null>.equals(<object that might be null>);equalsnull
9赞 Erin Drummond 10/1/2013
这是我见过的第一个真正有意义的尤达条件的例子
7赞 Oliver Watkins 5/12/2014
引发 NullPointerExceptions 是有原因的。之所以抛出它们,是因为对象在不应该出现的地方为 null。程序员的工作是解决这个问题,而不是隐藏问题。
0赞 Arnab Datta 11/26/2015
这只会掩盖问题。如果您以后使用怎么办?如果变量不应该为 null,而是......你的应用需要获得 NPE,这样你作为开发人员才能真正解决根本原因。foo
23赞 3 revs, 2 users 91%OscarRyz #10

我已经尝试过了,但对我来说并不总是最好的方法。有时,“不采取行动”是不合适的。NullObjectPattern

NullPointerException是一个运行时异常,这意味着它是开发人员的错,并且有足够的经验它会告诉你错误在哪里。

现在来回答:

尝试使所有属性及其访问器尽可能私有,或者完全避免将它们暴露给客户端。当然,您可以在构造函数中包含参数值,但是通过缩小范围,您不会让客户端类传递无效值。如果需要修改这些值,可以随时创建一个新的 .只需检查构造函数中的值一次,在其余方法中,几乎可以确定这些值不为 null。object

当然,经验是理解和应用这一建议的更好方法。

字节!

81赞 3 revs, 3 users 83%user2427 #11

Google 集合框架提供了一种实现 null 检查的良好而优雅的方法。

库类中有一个方法,如下所示:

static <T> T checkNotNull(T e) {
   if (e == null) {
      throw new NullPointerException();
   }
   return e;
}

用法是(与):import static

...
void foo(int a, Person p) {
   if (checkNotNull(p).getAge() > a) {
      ...
   }
   else {
      ...
   }
}
...

或者在您的示例中:

checkNotNull(someobject).doCalc();

评论

75赞 mysomic 5/19/2009
嗯,有什么区别?p.getAge() 将抛出相同的 NPE,但开销更少,堆栈跟踪更清晰。我错过了什么?
16赞 Thorbjørn Ravn Andersen 5/26/2009
最好在示例中抛出 IllegalArgumentException(“e == null”),因为它清楚地表明它是程序员预期的异常(以及足够的信息来实际允许维护者识别问题)。NullPointerExceptions 应该保留给 JVM,因为它清楚地表明这是无意的(通常发生在难以识别的地方)
6赞 Steven Benitez 2/14/2011
这现在是 Google Guava 的一部分。
36赞 Alex Worden 4/24/2012
对我来说,闻起来像是过度工程。只要让 JVM 抛出一个 NPE,不要用这种垃圾弄乱你的代码。
6赞 Cory Kendall 11/22/2012
我喜欢它并打开大多数方法和构造函数,并对参数进行显式检查;如果出现错误,方法总是在前几行失败,我知道有问题的引用,但没有找到类似的东西getThing().getItsThing().getOtherThing().wowEncapsulationIsBroken().setLol("hi");
256赞 5 revs, 3 users 82%erickson #12

哇,当我们有 57 种不同的方法来推荐 时,我几乎不想添加另一个答案,但我认为一些对这个问题感兴趣的人可能想知道 Java 7 有一个建议,即添加“null-safe handling”——if-not-equal-null 逻辑的简化语法。NullObject pattern

Alex Miller 给出的示例如下所示:

public String getPostcode(Person person) {  
  return person?.getAddress()?.getPostcode();  
}  

如果左标识符不为 null,则仅取消引用左标识符,否则将表达式的其余部分计算为 。有些人,比如 Java Posse 成员 Dick Wall 和 Devoxx 的选民非常喜欢这个提议,但也有反对意见,理由是它实际上会鼓励更多地使用作为哨兵值。?.nullnull


更新:Java 7 中 null 安全运算符的官方提案已在 Project Coin 下提交。语法与上面的示例略有不同,但概念相同。


更新:空安全算子提案没有进入 Project Coin。因此,您不会在 Java 7 中看到这种语法。

评论

23赞 Thorbjørn Ravn Andersen 5/26/2009
我认为这是错误的。应该有一种方法可以指定给定变量始终为非 null。
6赞 Boris Terzic 8/30/2009
更新:该提案不会使 Java7.请参见 blogs.sun.com/darcy/entry/project_coin_final_five
11赞 Rob 5/16/2012
有趣的想法,但语法的选择是荒谬的;我不希望每个关节都钉上一个充满问号的代码库。
8赞 Muhd 2/16/2013
这个运算符存在于 Groovy 中,所以那些想要使用它的人仍然可以选择它。
11赞 Victor - Reinstate Monica 10/2/2015
这是我见过的最巧妙的想法。它应该被添加到C语法的每一种合理的语言中。我宁愿到处“钉问号”,也不愿整天滚动浏览屏幕上的线条或躲避“保护条款”。
53赞 2 revs, 2 users 75%Michael Borgwardt #13

最终,完全解决这个问题的唯一方法是使用不同的编程语言:

  • 在 Objective-C 中,您可以执行等效于 调用方法 的操作,并且绝对不会发生任何事情。这使得大多数 null 检查变得不必要,但它会使错误更难诊断。nil
  • Nice(一种 Java 派生语言)中,所有类型都有两个版本:潜在 null 版本和非 null 版本。只能对非 null 类型调用方法。通过显式检查 null,可以将潜在的 null 类型转换为非 null 类型。这样可以更容易地知道哪些地方需要 null 检查,哪些地方不需要。

评论

5赞 mtsahakis 9/18/2018
我不熟悉 Nice,但 Kotlin 实现了同样的想法,将可为 null 和非 null 类型构建到语言的类型系统中。比 Optionals 或 null Object 模式简洁得多。
211赞 10 revs, 3 users 87%thSoft #14

如果不允许使用未定义的值:

您可以将 IDE 配置为警告您潜在的 null 取消引用。例如,在 Eclipse 中,请参阅 Java > Compiler > Errors/Warnings/Null 分析>首选项

如果允许未定义的值:

如果要定义一个新 API,其中未定义的值是有意义的,请使用 Option Pattern(可能熟悉函数式语言)。它具有以下优点:

  • 在 API 中明确说明是否存在输入或输出。
  • 编译器会强制您处理“未定义”的情况。
  • Option 是一个 monad,因此不需要进行冗长的 null 检查,只需使用 map/foreach/getOrElse 或类似的组合器来安全地使用该值(示例)。

Java 8 有一个内置的 Optional 类(推荐);对于早期版本,有一些库替代,例如 GuavaOptionalFunctionalJavaOption。但就像许多函数式模式一样,在 Java(甚至 8)中使用 Option 会产生相当多的样板,您可以使用不太冗长的 JVM 语言(例如 Scala 或 Xtend)来减少样板。

如果你必须处理一个可能返回 null 的 API,那么你在 Java 中就无能为力了。Xtend 和 Groovy 具有 Elvis 运算符和 null 安全取消引用运算符,但请注意,在 null 引用的情况下,这将返回 null,因此它只是“推迟”对 null 的正确处理。?:?.

评论

23赞 Ben Hardy 5/21/2011
事实上,期权模式很棒。存在一些 Java 等效项。Guava 包含一个称为 Optional 的有限版本,它省略了大部分功能。在 Haskell 中,这种模式称为 Maybe。
0赞 thSoft 9/12/2011
@Luca Molteni:你说得很对,我已经在帖子上发表了评论,要求扩展它。:)
10赞 Pierre Henry 4/25/2013
Java 8 中将提供一个可选类
1赞 thSoft 4/25/2013
...它(还没有)地图也没有 flatMap:download.java.net/jdk8/docs/api/java/util/Optional.html
3赞 Boann 11/4/2013
可选模式不能解决任何问题;现在你有两个,而不是一个潜在的 null 对象。
709赞 9 revs, 8 users 77%Luca Molteni #15

如果您使用(或计划使用)像 JetBrains IntelliJ IDEA、EclipseNetbeans 这样的 Java IDE,或者像 findbugs 这样的工具,那么您可以使用注解来解决这个问题。

基本上,你有和.@Nullable@NotNull

您可以在方法和参数中使用,如下所示:

@NotNull public static String helloWorld() {
    return "Hello World";
}

@Nullable public static String helloWorld() {
    return "Hello World";
}

第二个示例不会编译(在 IntelliJ IDEA 中)。

在另一段代码中使用第一个函数时:helloWorld()

public static void main(String[] args)
{
    String result = helloWorld();
    if(result != null) {
        System.out.println(result);
    }
}

现在 IntelliJ IDEA 编译器会告诉你检查是无用的,因为该函数永远不会返回 。helloWorld()null

使用参数

void someMethod(@NotNull someParameter) { }

如果你写这样的话:

someMethod(null);

这不会编译。

最后一个示例使用@Nullable

@Nullable iWantToDestroyEverything() { return null; }

执行此操作

iWantToDestroyEverything().something();

你可以肯定这不会发生。:)

这是一个很好的方法,可以让编译器检查比平时更多的东西,并强制执行你的合约,使其更强大。不幸的是,并非所有编译器都支持它。

在 IntelliJ IDEA 10.5 及更高版本中,他们添加了对任何其他实现的支持。@Nullable@NotNull

请参阅博客文章更灵活和可配置的 @Nullable/@NotNull 注释

评论

132赞 Jacek S 5/9/2010
@NotNull和其他空注解是 JSR 305 的一部分。您还可以使用它们来检测 FindBugs 等工具的潜在问题。@Nullable
34赞 Jonik 7/1/2011
我发现 & 接口位于包中,这很奇怪。(我想我将 com.sun 与有关使用专有 API 的警告联系起来。@NotNull@Nullablecom.sun.istack.internal
21赞 Java Ka Baby 9/8/2011
代码可移植性在 JetBrains 中消失了。在绑定到 ide 级别之前,我会三思而后行。就像 Jacek S 所说的那样,它们无论如何都是 JSR 的一部分,顺便说一下,我认为是 JSR303。
13赞 Shivan Dragon 8/19/2012
我真的不认为使用自定义编译器是解决这个问题的可行方法。
70赞 Armen Michaeli 3/20/2013
注解的好处是,当源代码是由一个不理解它们的系统构建时,它们会很好地降级。因此,实际上,代码不可移植的论点可能是无效的 - 如果你使用一个支持和理解这些注释的系统,你会得到更严格的错误检查的额外好处,否则你得到的错误更少,但你的代码仍然应该构建得很好,并且你正在运行的程序的质量是一样的, 因为无论如何,这些注释都不会在运行时强制执行。此外,所有编译器都是自定义的;-)@NotNull@Nullable
0赞 2 revs, 2 users 77%daniel #16

对于实用程序类,可以检查参数是否为空。

在所有其他情况下,您可能不必这样做。尽可能多地使用封装,从而减少您想要检查 null 的地方。

38赞 2 revs, 2 users 91%fastcodejava #17

除了使用之外,您还可以使用以下功能:assert

if (someobject == null) {
    // Handle null here then move on.
}

这比以下情况略好:

if (someobject != null) {
    .....
    .....



    .....
}

评论

2赞 Matthias Meid 8/17/2010
嗯,为什么呢?请不要有任何防御,我只是想了解更多关于 Java :)
9赞 Tyler 6/22/2011
@Mudu 作为一般规则,我更喜欢 if 语句中的表达式是更“积极”的陈述,而不是“消极”的陈述。因此,如果我看到,我会倾向于将其重构为(尽管有人可能会争辩说这是更积极的选择......但更重要的是,代码的重要部分没有包装在 s 中,并且大多数方法的缩进量减少了一级。我不知道这是否是fastcodejava的推理,但那将是我的。if (!something) { x(); } else { y(); }if (something) { y(); } else { x(); }!= null{}
2赞 Koray Tugay 12/16/2015
这也是我倾向于做的事情。在我的意见中保持代码干净。
0赞 Skaldebane 6/23/2020
@MatrixFrog是的,这使得代码更简洁且易于阅读,因为您首先看到重要的代码(当一切正常运行时没有错误)。
11赞 2 revs, 2 users 67%tltester #18
public static <T> T ifNull(T toCheck, T ifNull) {
    if (toCheck == null) {
           return ifNull;
    }
    return toCheck;
}

评论

2赞 Sawyer 8/17/2011
这种方法有什么问题,我认为@tltester只想在null的情况下给出默认值,这是有道理的。
1赞 ivant 4/16/2015
Apache commons-lang 中有这样一个方法: .还有一个更通用的: ,可用于实施降级策略:ObjectUtils.defaultIfNull()ObjectUtils.firstNonNull()firstNonNull(bestChoice, secondBest, thirdBest, fallBack);
-5赞 LarryN #19

另一个建议是防御性编程 - 其中类/函数提供已知且安全的默认值,并且 null 保留用于真正的错误/异常。

例如,当出现问题时,不要返回返回 null 的 String(例如将数字转换为字符串),而是让它们返回空字符串 (“”)。在继续操作之前,您仍然需要测试返回值,但不会有异常的特殊情况。这种编程方式的另一个好处是,程序将能够区分正常操作和异常并做出相应的响应。

评论

6赞 Mechanical snail 8/17/2012
-1.那就更糟了。现在,如果您忘记测试,您的程序不会崩溃,而是以静默方式传播不正确的值。
9赞 2 revs, 2 users 67%Leen Toelen #20

您可以使用 FindBugs。他们还有一个 Eclipse 插件),可以帮助您找到重复的 null 检查(除其他外),但请记住,有时您应该选择防御性编程。还有 Java 合同可能会有所帮助。

40赞 3 revs, 3 users 79%Oleg #21

确实是 Java 中的常见“问题”。

首先,我对此的看法:

我认为在NULL 不是有效值的情况下传递 NULL 时“吃”一些东西是不好的。如果您没有以某种错误退出方法,则意味着您的方法没有出错,这是不正确的。那么在这种情况下,你可能会返回 null,在接收方法中,你再次检查 null,它永远不会结束,你最终会得到“if != null”,等等。

因此,恕我直言,null 必须是一个严重错误,它会阻止进一步执行(即,null 不是有效值)。

我解决这个问题的方法是这样的:

首先,我遵循以下约定:

  1. 所有公共方法/API 始终检查其参数是否为 null
  2. 所有私有方法都不会检查 null,因为它们是受控方法(如果上面没有处理,就让 nullpointer 异常死亡)
  3. 唯一不检查 null 的其他方法是实用程序方法。它们是公共的,但如果您出于某种原因调用它们,您就会知道您传递了哪些参数。这就像试图在不提供水的情况下将水壶中的水烧开......

最后,在代码中,公共方法的第一行是这样的:

ValidationUtils.getNullValidator().addParam(plans, "plans").addParam(persons, "persons").validate();

请注意,addParam() 返回 self,以便您可以添加更多参数进行检查。

如果任何参数为 null,方法将抛出 checked(checked 或 unchecked 更像是设计/品味问题,但 my 被检查)。validate()ValidationExceptionValidationException

void validate() throws ValidationException;

例如,如果“plans”为 null,则消息将包含以下文本:

"参数 [plans] 遇到非法参数值 null"

正如你所看到的,addParam() 方法(字符串)中的第二个值是用户消息所必需的,因为即使有反射,你也无法轻松检测传入的变量名(反正不是这篇文章的主题......

是的,我们知道,超过这一行,我们将不再遇到 null 值,因此我们只是安全地在这些对象上调用方法。

这样,代码就干净、易于维护和可读。

评论

3赞 Paul Jackson 11/14/2011
绝对。仅抛出错误和崩溃的应用程序质量更高,因为毫无疑问,它们何时无法正常工作。吞下错误的应用程序充其量会正常降级,但通常不会以难以注意到且无法修复的方式工作。当发现问题时,它们更难调试。
95赞 3 revs, 2 users 75%Alex Worden #22

我是“快速失败”代码的粉丝。问问自己 - 在参数为 null 的情况下,您是否在做一些有用的事情?如果你对你的代码在这种情况下应该做什么没有一个明确的答案......即 - 它首先不应该是 null,然后忽略它并允许抛出 a。调用代码对 NPE 的意义与对 的意义一样大,但是如果抛出 NPE,开发人员会更容易调试和理解出了什么问题,而不是您的代码试图执行其他一些意外的应急逻辑 - 这最终会导致应用程序失败。NullPointerExceptionIllegalArgumentException

评论

2赞 ianpojman 10/18/2012
最好使用断言,即 Contract.notNull(abc, “abc must be non-null, did did failed to load during xyz?”);- 这是一种比执行 if (abc!=null) { throw new RuntimeException...} 更紧凑的方法
25赞 2 revs, 2 users 74%Mr Palo #23

我喜欢 Nat Pryce 的文章。以下是链接:

在文章中,还有一个指向 Java 可能类型的 Git 存储库的链接,我觉得这很有趣,但我认为仅凭它并不能减少 检查代码膨胀。在互联网上做了一些研究后,我认为 != null 代码膨胀主要可以通过精心设计来减少。

评论

0赞 ivan.aguirre 7/13/2013
迈克尔·费瑟斯(Michael Feathers)写了一篇简短而有趣的文章,介绍了您提到的方法:manuelp.newsblur.com/site/424
0赞 Skaldebane 6/25/2020
如果说这个答案有什么我最欣赏的,那就是“精心设计”的技巧。无论是归结为编写代码、修复错误还是检查无效性,在代码中做出整体良好的设计和组织都是至关重要的,以避免我们今天仍然必须处理的许多冗余,因为很久以前做出的糟糕的设计选择......
6赞 2 revs, 2 users 93%jeha #24

另一种选择:

以下简单函数有助于隐藏空检查(我不知道为什么,但我还没有找到它作为同一公共库的一部分):

public static <T> boolean isNull(T argument) {
    return (argument == null);
}

你现在可以写

if (!isNull(someobject)) {
    someobject.doCalc();
}

这是IMO更好的表达方式。!= null

评论

8赞 TMN 11/23/2011
如果你经常要否定返回值,那么简单地写一个函数不是更好吗?这难道不是更清楚地表明你的意图吗?isNotNull
0赞 jeha 11/23/2011
@TMN:你是对的,理想情况下,我希望同时拥有 和 方法。isNullisNotNull
25赞 user1050755 3/7/2013
我看不出这比“someobject != null”更简洁。
0赞 IceArdor 5/12/2017
编译时和运行时开发工具(如 findbugs)将更难识别重复的 null 检查。另外,你最好希望 Java 内联 isNull 函数,
77赞 45 revs, 2 users 73%Mykhaylo Adamovych #25

Null 不是“问题”。它是完整建模工具集的组成部分。软件旨在模拟世界的复杂性,而 null 承担了它的负担。Null 表示 Java 中的“无数据”或“未知”。因此,将 null 用于这些目的是合适的。我不喜欢“Null 对象”模式;我认为它上升了“谁来守卫” 守护者的问题。
如果你问我女朋友叫什么名字,我会告诉你我没有女朋友。在 Java 语言中,我将返回 null。 另一种方法是抛出有意义的异常,以指示某些无法(或不想)解决的问题,并将其委托给堆栈中更高位置,以重试或向用户报告数据访问错误。

  1. 对于“未知问题”,请给出“未知答案”。(从业务角度来看,如果这是正确的,则为 null 安全)在使用方法之前在方法中检查一次 null 参数,可以使多个调用方在调用之前检查它们。

    public Photo getPhotoOfThePerson(Person person) {
        if (person == null)
            return null;
        // Grabbing some resources or intensive calculation
        // using person object anyhow.
    }
    

    上一个导致正常的逻辑流程,从我的照片库中获取不存在的女朋友的照片。

    getPhotoOfThePerson(me.getGirlfriend())
    

    它适合即将到来的新 Java API(展望未来)

    getPhotoByName(me.getGirlfriend()?.getName())
    

    虽然不为某些人找到存储在数据库中的照片是相当“正常的业务流程”,但我曾经在其他一些情况下使用如下对

    public static MyEnum parseMyEnum(String value); // throws IllegalArgumentException
    public static MyEnum parseMyEnumOrNull(String value);
    

    不要讨厌输入(在 Eclipse 中生成 javadoc)并为你的公共 API 编写三个额外的单词。对于除了那些不阅读文档的人来说,这将绰绰有余。<alt> + <shift> + <j>

    /**
     * @return photo or null
     */
    

    /**
     * @return photo, never null
     */
    
  2. 这是相当理论上的情况,在大多数情况下,您应该更喜欢 java null 安全 API(以防它将在 10 年后发布),但 NullPointerExceptionException 的子类。因此,它是一种形式,指示合理的应用程序可能想要捕获的条件 (javadoc)!为了利用异常的第一个最大优势,并将错误处理代码与“常规”代码分开(根据 Java 的创建者的说法),对我来说,捕获 .ThrowableNullPointerException

    public Photo getGirlfriendPhoto() {
        try {
            return appContext.getPhotoDataSource().getPhotoByName(me.getGirlfriend().getName());
        } catch (NullPointerException e) {
            return null;
        }
    }
    

    可能会出现以下问题:

    Q.如果返回 null 怎么办?
    一个。这取决于业务逻辑。如果我找不到相册,我不会给你看照片。如果 appContext 未初始化怎么办?此方法的业务逻辑可以忍受这一点。如果相同的逻辑应该更严格,那么抛出异常,它是业务逻辑的一部分,应该使用对 null 的显式检查(案例 3)。新的 Java Null 安全 API 更适合于此,可以有选择地指定在程序员出错时初始化为快速失败的隐含内容和不隐含的内容。
    getPhotoDataSource()

    问:可以执行冗余代码,并可以获取不必要的资源。
    一个。如果尝试打开数据库连接,最后创建并使用人名作为 SQL 参数,则可能会发生这种情况。未知问题给出未知答案的方法(案例 1)在这里有效。在获取资源之前,该方法应检查参数并在需要时返回“未知”结果。
    getPhotoByName()PreparedStatement

    Q.由于尝试关闭打开,此方法会降低性能。
    答:软件首先要易于理解和修改。只有在这之后,人们才能考虑性能,而且只有在需要时!以及需要的地方!(来源)和许多其他人)。

    这种方法的使用方式与将错误处理代码与“常规”代码原则分开使用一样合理。请看下面的例子:

    public SomeValue calculateSomeValueUsingSophisticatedLogic(Predicate predicate) {
        try {
            Result1 result1 = performSomeCalculation(predicate);
            Result2 result2 = performSomeOtherCalculation(result1.getSomeProperty());
            Result3 result3 = performThirdCalculation(result2.getSomeProperty());
            Result4 result4 = performLastCalculation(result3.getSomeProperty());
            return result4.getSomeProperty();
        } catch (NullPointerException e) {
            return null;
        }
    }
    
    public SomeValue calculateSomeValueUsingSophisticatedLogic(Predicate predicate) {
        SomeValue result = null;
        if (predicate != null) {
            Result1 result1 = performSomeCalculation(predicate);
            if (result1 != null && result1.getSomeProperty() != null) {
                Result2 result2 = performSomeOtherCalculation(result1.getSomeProperty());
                if (result2 != null && result2.getSomeProperty() != null) {
                    Result3 result3 = performThirdCalculation(result2.getSomeProperty());
                    if (result3 != null && result3.getSomeProperty() != null) {
                        Result4 result4 = performLastCalculation(result3.getSomeProperty());
                        if (result4 != null) {
                            result = result4.getSomeProperty();
                        }
                    }
                }
            }
        }
        return result;
    }
    

    缴费灵。对于那些快速投反对票(而不是那么快阅读文档)的人,我想说我一生中从未遇到过空指针异常 (NPE)。但这种可能性是 Java 创建者有意设计的,因为 NPE 是 的子类。我们在 Java 历史上有一个先例,不是因为它实际上是一个应用程序错误,而仅仅是因为它不打算被捕获!NPE适合多少!但事实并非如此。ExceptionThreadDeathErrorErrorThreadDeath

  3. 仅当业务逻辑暗示时,才检查“无数据”。

    public void updatePersonPhoneNumber(Long personId, String phoneNumber) {
        if (personId == null)
            return;
        DataSource dataSource = appContext.getStuffDataSource();
        Person person = dataSource.getPersonById(personId);
        if (person != null) {
            person.setPhoneNumber(phoneNumber);
            dataSource.updatePerson(person);
        } else {
            Person = new Person(personId);
            person.setPhoneNumber(phoneNumber);
            dataSource.insertPerson(person);
        }
    }
    

    public void updatePersonPhoneNumber(Long personId, String phoneNumber) {
        if (personId == null)
            return;
        DataSource dataSource = appContext.getStuffDataSource();
        Person person = dataSource.getPersonById(personId);
        if (person == null)
            throw new SomeReasonableUserException("What are you thinking about ???");
        person.setPhoneNumber(phoneNumber);
        dataSource.updatePerson(person);
    }
    

    如果未初始化 appContext 或 dataSource,则未经处理的运行时 NullPointerException 将终止当前线程,并由 Thread.defaultUncaughtExceptionHandler 处理(用于定义和使用你喜欢的记录器或其他通知机制)。如果未设置,ThreadGroup#uncaughtException 会将堆栈跟踪打印到系统错误。应该监控应用程序错误日志,并为每个未处理的异常打开 Jira 问题,这实际上是应用程序错误。程序员应该在初始化内容中的某个地方修复错误。

评论

6赞 artbristol 9/14/2013
捕获和返回调试是可怕的。无论如何,你以后会得到 NPE,而且真的很难弄清楚最初什么是空的。NullPointerExceptionnull
25赞 Doval 11/20/2013
如果我有名声,我会投反对票。null 不仅不是必需的,而且是类型系统中的一个漏洞。将树分配给列表是一个类型错误,因为树不是列表类型的值;按照同样的逻辑,分配 null 应该是一个类型错误,因为 null 不是 Object 类型的值,也不是任何有用的类型。即使是发明 null 的人也认为这是他的“十亿美元错误”。“可能是 T 类型或无类型的值的值”的概念是它自己的类型,应该这样表示(例如 Maybe<T> 或 Optional<T>)。
2赞 Mike 4/1/2014
作为“也许<T>或可选<T>”,您仍然需要编写这样的代码,那么有什么区别?if (maybeNull.hasValue()) {...}if (maybeNull != null)) {...}
1赞 Mike 4/1/2014
截至“捕获 NullPointerException 并返回 null 调试是可怕的。无论如何,你以后都会得到NPE,而且真的很难弄清楚原来的空是什么。我完全同意!在这些情况下,你应该写十几个“if”语句,或者如果业务逻辑暗示数据就地,则抛出NPE,或者使用新Java中的null-safe运算符。但在某些情况下,我并不关心哪个确切步骤给我空。例如,在屏幕上显示之前为用户计算一些值,而您确实希望数据可能会丢失。
3赞 cHao 11/8/2014
@MykhayloAdamovych:在 ur 可能为 null 的情况下,或不是在它不应该为 null 的情况下。如果有一个类型显式表示“此值可能为 null -- 请谨慎使用”,并且一致地使用和返回此类类型,那么每当您在代码中看到普通的旧类型时,您都可以假定它永远不会为 null。(当然,如果编译器可以强制执行,这将更有用。Maybe<T>Optional<T>TT
32赞 2 revsMurat #26

Guava 是 Google 的一个非常有用的核心库,它有一个很好且有用的 API 来避免空值。我发现UsingAndAvoidingNullExplained非常有帮助。

正如 wiki 中所解释的:

Optional<T>是一种将可为 null 的 T 引用替换为 非 null 值。Optional 可以包含非空 T 引用 (在这种情况下,我们说引用是“存在的”),或者它可能包含 什么都没有(在这种情况下,我们说引用是“不存在的”)。它从来都不是 说“包含 null”。

用法:

Optional<Integer> possible = Optional.of(5);
possible.isPresent(); // returns true
possible.get(); // returns 5

评论

0赞 Tugalsan Karabacak 5/25/2023
get() 从来都不是一个好主意。您可以使用: val = funcThatReturnsOptional().orElse(CUSTOM_ERROR_DIFFERENT_USECASES);在一行中,并检查不同用例的值。
3赞 2 revs, 2 users 67%Jochen #27

您还可以使用 Checker Framework(使用 JDK 7 及更高版本)静态检查 null 值。这可能会解决很多问题,但需要运行一个目前仅适用于 OpenJDK AFAIK 的额外工具。https://checkerframework.org/

3赞 4 revs, 3 users 85%Alex Vaz #28

好吧,我现在在技术上已经回答了一百万次,但我不得不这么说,因为这是与 Java 程序员的无休止的讨论。

对不起,但我不同意几乎所有以上。我们必须在 Java 中测试 null 的原因是 Java 程序员不知道如何处理内存。

我这样说是因为我有很长的 C++ 编程经验,但我们不这样做。换句话说,你不需要。请注意,在 Java 中,如果你点击了一个悬空的指针,你会得到一个正常的异常;在 C++ 中,此异常通常不会被捕获并终止程序。

不想这样做?然后遵循一些简单的规则,ala C/C++。

不要那么容易地实例化事物,认为每个“新”都会给你带来很多麻烦,并遵循这些简单的规则。

一个类只能以 3 种方式访问内存 ->

  1. 它可以“拥有”类成员,他们将遵循以下规则:

    1. 所有“HAS”成员都是在构造函数中创建“new”的。
    2. 您将在析构函数或等效的 close() 中关闭 /de 分配 Java 中的函数,而不是其他类。

这意味着您需要牢记(就像 Java 一样)谁是每个资源的所有者或父级,并尊重该所有权。对象仅由创建对象的类删除。还有 ->

  1. 有些成员将被“使用”,但不是拥有或“拥有”。这是另一个类中的“OWN”,并作为参数传递给构造函数。由于这些归另一个类所有,我们永远不会删除或关闭它,只有父类可以。

  2. 类中的方法还可以实例化本地对象供内部使用,这些对象永远不会传递到类的一侧,或者它们应该是正常的“has”对象。

最后,要使所有这些工作都起作用,您需要有一个规范的设计,以层次结构形式进行类,并且不进行循环。

在这种设计下,并且遵循上述规则,层次结构设计中的子类不可能访问被销毁的指针,因为这意味着父类在子类之前被销毁,而分层非循环设计不允许这样做。

最后,还要记住,在启动系统时,您应该从层次结构的顶部到底部构建并销毁自下而上的销毁。您永远不会在任何地方出现空指针,或者有人违反规则。

评论

1赞 user1050755 3/7/2013
同意,尽管只是在概念上。是的,一个人应该有一个计划。但是,你需要能够制定计划并具有逻辑思考能力以及开发和维护一致模式的程序员。还有一些雇主的主要关注点是金钱和工资;然后开始查看 JPA 规范,以了解什么是一致的模式/计划,以及开发和/或记录它需要什么。
3赞 artbristol 9/15/2013
这个答案与C++相关,但与Java等垃圾回收语言无关。
0赞 Alex Vaz 9/24/2013
我恭敬地不同意。在JAVA(或几乎任何其他语言)中良好的内存处理将给你带来很多好处。从减少空指针时提高稳定性,减少内存占用时提高性能,到更好地控制关系时进行更好的设计。我认为,许多 JAVA 应用程序的草率内存管理会导致频繁的 NPE 和内存泄漏。
0赞 supercat 11/5/2013
我非常同意你所写的内容,并且认为 Java 没有区分封装所有权的引用和不封装所有权的引用,这是非常不幸的。那些认为“这就是 GC 的用途”的人忽略了一个关键点:GC 避免了不可变对象拥有所有者的需要,并且它还消除了对已删除对象的悬空引用变成对其他任意对象的引用的可能性(就像在 C++ 中可能发生的那样)。但是,如果多个对象将可变对象的状态封装为自己的一部分,则很难编写正确的代码。
0赞 supercat 11/5/2013
我更喜欢的思考方式是说引用可以封装身份、可变状态、两者兼而有之,或者两者都不是。此外,引用可以封装可变状态,因为它们标识了不可变对象,或者因为它们标识的对象永远不会暴露给任何可能改变它的东西。太糟糕了,Java 没有这种区别的概念,因为如果这样做,相等性检查和克隆之类的事情可以自动处理 99%。
72赞 3 revs, 2 users 67%Raedwald #29

Java 7 有一个新的实用程序类,上面有一个方法。如果其参数为 null,则这样做只是抛出一个,但它会稍微清理代码。例:java.util.ObjectsrequireNonNull()NullPointerException

Objects.requireNonNull(someObject);
someObject.doCalc();

该方法对于在构造函数中执行赋值之前进行检查最有用,每次使用它都可以保存三行代码:

Parent(Child child) {
   if (child == null) {
      throw new NullPointerException("child");
   }
   this.child = child;
}

成为

Parent(Child child) {
   this.child = Objects.requireNonNull(child, "child");
}

评论

9赞 user1050755 3/7/2013
实际上,您的示例构成了代码膨胀:第一行是多余的,因为 NPE 将在第二行中抛出。;-)
1赞 Stuart Marks 3/7/2013
真。一个更好的例子是,如果第二行是 。doCalc(someObject)
0赞 user1050755 3/7/2013
取决于。如果您是 doCalc() 的作者,我建议将检查放入该方法的正文中(如果可能的话)。然后你很可能会调用 someObject.someMethod(),同样不需要检查 null。:-)
0赞 Stuart Marks 3/9/2013
好吧,如果您不是 的作者,并且在给定 null 时它不会立即抛出 NPE,则需要检查 null 并自己抛出 NPE。这就是目的。doCalc()Objects.requireNonNull()
7赞 Rob Grant 9/26/2013
这不仅仅是代码膨胀。最好提前检查,而不是在导致副作用或使用时间/空间的方法中途进行检查。
1赞 drzymala #30

我更喜欢这个

public void simpleFunc(SomeObject someObject){
    someObject = someObject != null ? someObject : new SomeObject(null);
    someObject.doSomething();
}

当然,在我的示例中,SomeObject 优雅地处理一个 null 参数。例如,记录此类事件,然后不执行其他操作。

37赞 3 revs, 3 users 68%iangreen #31

只是永远不要使用 null。不允许。

在我的类中,大多数字段和局部变量都具有非 null 默认值,我在代码中的任何地方添加 contract 语句(always-on 断言)以确保强制执行(因为它更简洁,比让它作为 NPE 出现然后必须解析行号等更具表现力)。

一旦我采用了这种做法,我注意到问题似乎可以自行解决。你会在开发过程中更早地偶然发现一些事情,并意识到你有一个弱点。更重要的是..它有助于封装不同模块的关注点,不同的模块可以相互“信任”,并且不再在代码中乱扔构造!if = null else

这是防御性编程,从长远来看,这会导致代码更干净。始终清理数据,例如通过执行严格的标准,问题就会消失。

class C {
    private final MyType mustBeSet;
    public C(MyType mything) {
       mustBeSet=Contract.notNull(mything);
    }
   private String name = "<unknown>";
   public void setName(String s) {
      name = Contract.notNull(s);
   }
}


class Contract {
    public static <T> T notNull(T t) { if (t == null) { throw new ContractException("argument must be non-null"); return t; }
}

合约就像迷你单元测试,即使在生产环境中也一直在运行,当事情失败时,你知道为什么,而不是随机的NPE,你必须以某种方式弄清楚。

评论

0赞 ianpojman 11/1/2012
为什么会投反对票?根据我的经验,这远远优于其他方法,很想知道为什么不
0赞 ChrisBlom 7/27/2013
我同意,这种方法可以防止与空值相关的问题,而不是通过到处散布代码空值检查来修复它们。
9赞 Steve Kuo 12/17/2013
这种方法的问题在于,如果从未设置 name,则它的值为“<unknown>”,其行为类似于设置值。现在假设我需要检查名称是否从未设置过(未知),我必须与特殊值“<unknown>”进行字符串比较。
1赞 ianpojman 1/27/2015
史蒂夫的好点。我经常做的是将该值作为常量,例如 public static final String UNSET=“__unset” ...private String 字段 = UNSET ...然后私有布尔值 isSet() { return UNSET.equals(field);
0赞 TomasMolina 12/10/2017
恕我直言,这是 Null Object Pattern 的实现,带有 Optional (Contract) 的实现。它在持久性类类上的行为方式如何?我认为不适用于这种情况。
4赞 3 revs, 2 users 67%Peter Mortensen #32

可以在方法调用之前使用侦听器。这就是面向方面的编程所关注的。

假设 M1(对象测试)是一个方法,而 M2 是一个方法,我们在方法调用之前应用了一个方面。如果然后调用 M1,否则执行其他操作。它适用于您要为其应用某个方面的所有方法。如果要为实例字段和构造函数应用方面,可以使用 AspectJ方法方面,Spring 也可以是最佳选择。M2(Object test2)test2 != null

评论

0赞 TomasMolina 12/10/2017
您是否建议拦截整个应用程序的每种方法?
2赞 2 revs, 2 users 82%Vinay Lodha #33

首先,我们不能真正删除所有 null 条件。我们可以使用和注释(如前所述)来减少它们。但这需要一些框架的支持。这就是 OVal 可以提供帮助的地方。@NotNull@Nullable

基本思想是对象/参数/构造函数应始终满足前提条件。你可以有很多前提条件,比如 ,OVal 会注意对象在调用时应该处于一致的状态。NullableNotNull

我猜 OVal 内部使用 AspectJ 来验证前提条件。

@Guarded
public class BusinessObject
{
  public BusinessObject(@NotNull String name)
  {
    this.name = name;
  }

  ...
}

例如

// Throws a ConstraintsViolatedException because parameter name is null
BusinessObject bo = new BusinessObject(null);
185赞 4 revs, 4 users 72%Pierre Henry #34

Java 8 带来了一个可以说解决了一些问题的新类。至少可以说它提高了代码的可读性,并且在公共 API 的情况下,使 API 的契约对客户端开发人员来说更清晰。java.util.Optional

他们的工作方式是这样的:

给定类型 () 的可选对象被创建为方法的返回类型。它可以是空的,也可以包含一个对象:FruitFruit

public static Optional<Fruit> find(String name, List<Fruit> fruits) {
   for (Fruit fruit : fruits) {
      if (fruit.getName().equals(name)) {
         return Optional.of(fruit);
      }
   }
   return Optional.empty();
}

现在看一下这段代码,我们在给定的 Fruit 实例中搜索 () 列表:Fruitfruits

Optional<Fruit> found = find("lemon", fruits);
if (found.isPresent()) {
   Fruit fruit = found.get();
   String name = fruit.getName();
}

可以使用运算符对可选对象执行计算或从中提取值。 允许您为缺失值提供回退。map()orElse()

String nameOrNull = find("lemon", fruits)
    .map(f -> f.getName())
    .orElse("empty-name");

当然,对 null/empty 值的检查仍然是必要的,但至少开发人员意识到该值可能是空的,忘记检查的风险是有限的。

在从头开始构建的 API 中,每当返回值可能为空时,并且仅在不能返回时才返回纯对象(约定),客户端代码可能会放弃对简单对象返回值的 null 检查......Optionalnull

当然,也可以用作方法参数,在某些情况下,也许比 5 或 10 个重载方法更适合指示可选参数。Optional

Optional提供了其他方便的方法,例如允许使用默认值的方法,以及适用于 lambda 表达式的方法。orElseifPresent

我邀请您阅读这篇文章(我写这个答案的主要来源),其中很好地解释了(通常是空指针)问题以及(部分)解决方案:Java 可选对象NullPointerExceptionOptional

评论

12赞 Bradley Gottfried 5/28/2013
Google 的 guava 对 Java 6+ 有一个可选的暗示。
19赞 Cornel Masson 10/9/2014
需要强调的是,仅将 Optional 与 ifPresent() 一起使用不会比正常的 null 检查增加太多价值。它的核心价值在于它是一个可以在 map/flapMap 的函数链中使用的 monad,它实现了类似于其他地方提到的 Groovy 中的 Elvis 算子的结果。但是,即使没有这种用法,我发现 orElse/orElseThrow 语法也非常有用。
0赞 JohnC 12/9/2015
这篇博客在可选 winterbe.com/posts/2015/03/15/avoid-null-checks-in-java 上有一个很好的条目
6赞 Satyendra Kumar 3/7/2018
为什么人们倾向于这样做而不是if(optional.isPresent()){ optional.get(); }optional.ifPresent(o -> { ...})
1赞 crush 8/10/2018
因此,除了 API 契约提示之外,它实际上只是为了迎合那些喜欢无休止地链接方法的函数式程序员。
16赞 2 revsStuart Axon #35
  1. 切勿将变量初始化为 null。
  2. 如果 (1) 不可行,则将所有集合和数组初始化为空集合/数组。

在您自己的代码中执行此操作,您可以避免 != null 检查。

大多数时候,null 检查似乎保护集合或数组的循环,因此只需将它们初始化为空,您就不需要任何 null 检查。

// Bad
ArrayList<String> lemmings;
String[] names;

void checkLemmings() {
    if (lemmings != null) for(lemming: lemmings) {
        // do something
    }
}



// Good
ArrayList<String> lemmings = new ArrayList<String>();
String[] names = {};

void checkLemmings() {
    for(lemming: lemmings) {
        // do something
    }
}

这其中的开销很小,但对于更干净的代码和更少的 NullPointerExceptions 来说,这是值得的。

评论

3赞 ggb667 9/23/2013
+1 我同意这一点。永远不应返回初始化一半的对象。Jaxb 相关代码和 bean 代码对此是 notiroius。这是不好的做法。所有集合都应初始化,并且所有对象都应存在(理想情况下)没有空引用。考虑一个包含集合的对象。检查对象是否为 null、集合是否为 null 以及集合是否不包含 null 对象是不合理和愚蠢的。
1赞 2 revsiowatiger08 #36

我们一直在使用 Apache 库 (Apache Commons) 来解决这个问题。

ObjectUtils.equals(object, null)

CollectionUtils.isEmpty(myCollection);

StringUtils.isEmpty("string");

我喜欢前面的答案,作为一种做法,为集合提供初始默认值或空集,以最大程度地减少需求。

这些可能是简单的用法,可防止您具有 NullPointerException 或使用空集合。这并不能回答如何处理 null 对象的问题,但这些对象为对象或集合的基本验证提供了一些检查。

希望这会有所帮助。

评论

1赞 Steve Kuo 7/11/2014
我不喜欢,因为他们也做了一个空检查,方法名称没有暗示。CollectionUtils.isEmptyStringUtils.isEmpty
2赞 Tobb #37

避免不必要的方法很简单:null-checks

You need to know which variables can be null, and which cannot, and you need to be confident about which category a given variable fall into.

但是,虽然可以简单地说出来,但实现它却更难。关键在于部分,因为你怎么能确定一个变量不能为空呢?confident

这个问题没有快速解决的简单答案,但这里有一些提示:

  1. 干净的代码。能够推理一段代码的行为,最重要的是它是用易于理解的问题编写的。根据变量所代表的内容命名变量,以它们的作用命名方法,应用 (in : http://en.wikipedia.org/wiki/SOLID_(object-oriented_design),这意味着每段代码都应该有一个职责,并且只做这个,不做别的)。一旦你的代码是干净的,就更容易推理它,也可以跨多个代码层/层。对于杂乱无章的代码,试图理解方法的作用可能会让你忘记你为什么要阅读这个方法。(提示:阅读 Robert C. Martin 的“Clean Code”)Single responsibility principleSSOLID

  2. 避免返回值。如果某个值会阻止程序正常运行,请抛出一个值(确保添加适当的错误处理)。例如,返回值可能是可以接受的,例如尝试从数据库中获取对象。在这些情况下,请编写处理这些值的代码,并在耳后记下,这里有一些可能会返回的内容。处理返回的值尽可能接近返回方法的调用方(不要盲目地将其传递回调用链。nullnullexceptionnullnullnullnullnull

  3. 永远不要将显式值作为参数传递(至少不要跨类传递)。如果您曾经处于传递 -parameter 是唯一选项的位置,那么创建一个没有此参数的新方法是要走的路。nullnull

  4. 验证您的输入!确定应用程序的“入口点”。它们可以包括 Web 服务、REST 服务、远程 EJB 类、控制器等。对于这些入口点中的每个方法,问问自己:“如果此参数为 null,此方法是否正确执行?如果答案是否定的,请添加 。如果缺少所需的参数,这将抛出一个。在入口点中进行这种类型的验证的好处是,您可以轻松地在从入口点执行的代码中假设此变量永远不会为空!此外,如果这失败了,在入口点,调试比你只是深入了解你的代码要容易得多,因为这样的失败只能意味着一件事:客户端没有向你发送所有必需的信息。在大多数情况下,您希望验证所有输入参数,如果您发现自己处于需要允许大量值的位置,则可能是接口设计不佳的迹象,需要重构/添加以满足客户端的需求。Validate.notNull(someParam, "Can't function when someParam is null!");IllegalArgumentExceptionNullPointerExceptionnull

  5. 使用 s 时,返回一个空的而不是 null!Collection

  6. 使用数据库时,请使用 -constraints。这样,您就会知道从数据库中读取的值不能为 null,并且不必检查它。not null

  7. 构建您的代码并坚持下去。这样做可以对代码的行为做出假设,例如,如果应用程序的所有输入都经过验证,则可以假设这些值永远不会为 null。

  8. 如果您还没有这样做,请编写代码的自动测试。通过编写测试,您将对代码进行推理,并且您还将更加确信它能完成它应该做的事情。此外,自动化测试可以保护您在重构过程中避免出错,因为它会立即让您知道这段代码没有像以前那样做。

当然,您仍然需要进行 null 检查,但它可以减少到最低限度(即知道您可能会得到 null 值的情况,而不是为了确定而无处不在。当涉及到空检查时,我实际上更喜欢使用三元运算符(但要小心使用,当你开始嵌套它们时,它们会变得非常混乱。

public String nullSafeToString(final Object o) {
    return o != null ? o.toString() : "null";
}
18赞 2 revs, 2 users 96%Alireza Fattahi #38

请允许我更笼统地回答这个问题!

当方法以我们意想不到的方式获取参数时,我们通常会遇到这个问题(错误的方法调用是程序员的错)。例如:你希望得到一个对象,但你得到的是一个 null。你希望得到一个至少有一个字符的字符串,而不是你得到一个空的字符串...

所以两者之间没有区别:

if(object == null){
   //you called my method badly!

}

if(str.length() == 0){
   //you called my method badly again!
}

他们都希望在执行任何其他函数之前确保我们收到了有效的参数。

正如其他一些答案中提到的,为了避免上述问题,您可以遵循按合同设计模式。请参阅 http://en.wikipedia.org/wiki/Design_by_contract

要在 java 中实现此模式,您可以使用核心 java 注解(如 javax.annotation.NotNull)或使用更复杂的库(如 Hibernate Validator)。

举个例子:

getCustomerAccounts(@NotEmpty String customerId,@Size(min = 1) String accountType)

现在,您可以安全地开发方法的核心功能,而无需检查输入参数,它们可以保护您的方法免受意外参数的影响。

您可以更进一步,确保在应用程序中只能创建有效的 pojo。(来自 Hibernate 验证器站点的示例)

public class Car {

   @NotNull
   private String manufacturer;

   @NotNull
   @Size(min = 2, max = 14)
   private String licensePlate;

   @Min(2)
   private int seatCount;

   // ...
}

评论

0赞 Tomas 10/30/2015
javax根据定义,它不是“核心 Java”。
6赞 3 revs, 2 users 74%Gal Morad #39

我发现番石榴先决条件在这种情况下非常有用。我不喜欢将 null 留给 null 指针异常,因为理解 NPE 的唯一方法是找到行号。生产版本和开发版本中的行号可以不同。

使用 Guava 前提条件,我可以检查 null 参数并在一行中定义有意义的异常消息。

例如

Preconditions.checkNotNull(paramVal, "Method foo received null paramVal");
15赞 2 revs, 2 users 81%Sireesh Yarlagadda #40

这是大多数开发人员最常遇到的错误。

我们有很多方法可以解决这个问题。

方法1:

org.apache.commons.lang.Validate //using apache framework

notNull(Object 对象,字符串消息)

方法2:

if(someObject!=null){ // simply checking against null
}

方法3:

@isNull @Nullable  // using annotation based validation

方法4:

// by writing static method and calling it across whereever we needed to check the validation

static <T> T isNull(someObject e){  
   if(e == null){
      throw new NullPointerException();
   }
   return e;
}

评论

0赞 pkalinow 2/3/2016
广告 4.它不是很有用 - 当你检查指针是否为 null 时,你可能想在它上面调用一个方法。在 null 上调用方法会给出相同的行为 - NullPointerException。
0赞 fozzybear 3/23/2023
方法 4 是多余的,已经包含在Objects.requireNonNull()
17赞 4 revsluke1985 #41

我完全无视建议在每种情况下使用 null 对象的答案。这种模式可能会破坏契约,将问题埋得越来越深,而不是解决问题,更不用说使用不当会产生另一堆需要未来维护的样板代码。

实际上,如果从方法返回的内容可以为 null,并且调用代码必须对此做出决定,则应该有一个较早的调用来确保状态。

还要记住,如果不小心使用,null 对象模式将占用大量内存。为此,NullObject 的实例应该在所有者之间共享,而不是每个所有者的单一实例。

此外,我不建议使用这种模式,其中类型是原始类型表示 - 就像数学实体一样,它们不是标量:向量、矩阵、复数和 POD(Plain Old Data) 对象,它们旨在以 Java 内置类型的形式保存状态。在后一种情况下,您最终会调用具有任意结果的 getter 方法。例如,NullPerson.getName() 方法应该返回什么?

为了避免荒谬的结果,值得考虑这种情况。

评论

0赞 pkalinow 2/3/2016
带有“hasBackground()”的解决方案有一个缺点 - 它不是线程安全的。如果需要调用两个方法而不是一个方法,则需要在多线程环境中同步整个序列。
0赞 luke1985 2/3/2016
@pkalinow 你做了一个人为的例子,只是为了指出这个解决方案有一个缺点。如果代码不打算在多线程应用程序中运行,那么就没有缺点。我可以把你90%的代码放在你身上,这些代码不是线程安全的。我们在这里谈论的不是代码的这一方面,而是设计模式。多线程本身就是一个主题。
0赞 pkalinow 2/4/2016
当然,在单线程应用程序中,这不是问题。我之所以发表评论,是因为有时这是一个问题。
0赞 luke1985 2/4/2016
@pkalinow 如果你仔细研究这个主题,你会发现 Null Object 设计模式并不能解决多线程问题。所以这无关紧要。老实说,我已经找到了适合这种模式的地方,所以我最初的答案实际上有点错误。
28赞 2 revs, 2 users 87%Yogesh Devatraj #42

对于每个 Java 开发人员来说,这是一个非常常见的问题。因此,Java 8 中提供了官方支持,可以在没有杂乱代码的情况下解决这些问题。

Java 8 引入了 .它是一个容器,可能包含也可能不包含非 null 值。Java 8 提供了一种更安全的方法来处理在某些情况下值可能为 null 的对象。它的灵感来自HaskellScala的思想。java.util.Optional<T>

简而言之,Optional 类包含显式处理值存在或不存在的情况的方法。但是,与 null 引用相比,优点是 Optional<T> 类强制您考虑值不存在时的情况。因此,可以防止意外的 null 指针异常。

在上面的示例中,我们有一个家庭服务工厂,该工厂将句柄返回给家庭中可用的多个设备。但这些服务可能可用,也可能不可用;这意味着它可能会导致 NullPointerException。让我们在使用任何服务之前不要添加 null 条件,而是将其包装到 Optional<Service> 中。if

包装到 OPTION<T>

让我们考虑一种从工厂获取服务引用的方法。不要返回服务引用,而是将其包装为 Optional。它让 API 用户知道返回的服务可能可用,也可能不可用/功能,防御性使用

public Optional<Service> getRefrigertorControl() {
      Service s = new  RefrigeratorService();
       //...
      return Optional.ofNullable(s);
   }

如您所见,提供了一种简单的方法来包装引用。还有另一种方法可以获取 Optional 的引用,或者 & 。一个用于返回空对象而不是重新调整 null,另一个用于包装不可为 null 的对象。Optional.ofNullable()Optional.empty()Optional.of()

那么,究竟如何帮助避免空检查呢?

包装引用对象后,Optional 提供了许多有用的方法,用于在没有 NPE 的情况下调用包装引用上的方法。

Optional ref = homeServices.getRefrigertorControl();
ref.ifPresent(HomeServices::switchItOn);

如果给定的 Consumer 是非 null 值,则 Optional.ifPresent 使用引用调用给定的 Consumer。否则,它不执行任何操作。

@FunctionalInterface
public interface Consumer<T>

表示接受单个输入参数且不返回任何结果的操作。与大多数其他功能接口不同,消费者需要通过副作用来操作。 它是如此干净且易于理解。在上面的代码示例中,如果 Optional 持有引用为非 null,则调用。HomeService.switchOn(Service)

我们经常使用三元运算符来检查 null 条件并返回替代值或默认值。Optional 提供了另一种在不检查 null 的情况下处理相同条件的方法。Optional.orElse(defaultObj) 如果 Optional 具有 null 值,则返回 defaultObj。让我们在示例代码中使用它:

public static Optional<HomeServices> get() {
    service = Optional.of(service.orElse(new HomeServices()));
    return service;
}

现在 HomeServices.get() 做了同样的事情,但方式更好。它检查服务是否已初始化为未初始化。如果是,则返回相同的内容或创建新的新服务。Optional<T>.orElse(T) 有助于返回默认值。

最后,这里是我们的 NPE 以及 null 免检查代码:

import java.util.Optional;
public class HomeServices {
    private static final int NOW = 0;
    private static Optional<HomeServices> service;

public static Optional<HomeServices> get() {
    service = Optional.of(service.orElse(new HomeServices()));
    return service;
}

public Optional<Service> getRefrigertorControl() {
    Service s = new  RefrigeratorService();
    //...
    return Optional.ofNullable(s);
}

public static void main(String[] args) {
    /* Get Home Services handle */
    Optional<HomeServices> homeServices = HomeServices.get();
    if(homeServices != null) {
        Optional<Service> refrigertorControl = homeServices.get().getRefrigertorControl();
        refrigertorControl.ifPresent(HomeServices::switchItOn);
    }
}

public static void switchItOn(Service s){
         //...
    }
}

完整的帖子是 NPE 以及 Null 免检查代码......真的吗?

评论

0赞 KrishPrabakar 1/2/2020
上面的代码中有一个空检查 - 可以更改为if(homeServices != null) {homeServices.ifPresent(h -> //action);
0赞 2 revsMahdi El Masaoudi #43

您可以使用 JUnit 等框架将类与单元测试耦合。 这样,您的代码将是干净的(没有无用的检查),并且您将确保您的实例不会为 null。

这是使用单元测试的一个很好的理由。

1赞 2 revsLii #44

使用 Java 8 lambda 可以定义 util 方法,以几乎漂亮的方式处理嵌套的 null 检查。

void example() {
    Entry entry = new Entry();
    // This is the same as H-MANs solution 
    Person person = getNullsafe(entry, e -> e.getPerson());    
    // Get object in several steps
    String givenName = getNullsafe(entry, e -> e.getPerson(), p -> p.getName(), n -> n.getGivenName());
    // Call void methods
    doNullsafe(entry, e -> e.getPerson(), p -> p.getName(), n -> n.nameIt());        
}

/** Return result of call to f1 with o1 if it is non-null, otherwise return null. */
public static <R, T1> R getNullsafe(T1 o1, Function<T1, R> f1) {
    if (o1 != null) return f1.apply(o1);
    return null; 
}

public static <R, T0, T1> R getNullsafe(T0 o0, Function<T0, T1> f1, Function<T1, R> f2) {
    return getNullsafe(getNullsafe(o0, f1), f2);
}

public static <R, T0, T1, T2> R getNullsafe(T0 o0, Function<T0, T1> f1, Function<T1, T2> f2, Function<T2, R> f3) {
    return getNullsafe(getNullsafe(o0, f1, f2), f3);
}


/** Call consumer f1 with o1 if it is non-null, otherwise do nothing. */
public static <T1> void doNullsafe(T1 o1, Consumer<T1> f1) {
    if (o1 != null) f1.accept(o1);
}

public static <T0, T1> void doNullsafe(T0 o0, Function<T0, T1> f1, Consumer<T1> f2) {
    doNullsafe(getNullsafe(o0, f1), f2);
}

public static <T0, T1, T2> void doNullsafe(T0 o0, Function<T0, T1> f1, Function<T1, T2> f2, Consumer<T2> f3) {
    doNullsafe(getNullsafe(o0, f1, f2), f3);
}


class Entry {
    Person getPerson() { return null; }
}

class Person {
    Name getName() { return null; }
}

class Name {
    void nameIt() {}
    String getGivenName() { return null; }
}

(这个答案首先发布在这里

3赞 sidgate #45

Java 8 现在有 Optional 类,它包装了考虑的对象,如果存在值,isPresent() 将返回 true,get() 将返回该值。

http://www.oracle.com/technetwork/articles/java/java8-optional-2175753.html

8赞 3 revsRavindra babu #46

我遵循以下准则以避免空检查。

  1. 尽可能避免成员变量的延迟初始化。初始化声明本身中的变量。这将处理 NullPointerExceptions。

  2. 在周期的早期确定成员变量的可变性。有效地使用关键字等语言结构。final

  3. 如果您知道 method 的扩充不会更改,请将它们声明为 .final

  4. 尽可能限制数据的突变。某些变量可以在构造函数中创建,并且永远无法更改。删除公共 setter 方法,除非它们确实是必需的。

    例如,假设应用程序 () 中的一个类正在维护一个类似 的集合。不要在 A.java 中提供 getter 方法,并允许直接在 中添加元素。而是在 中提供一个 API,该 API 将元素添加到集合中。A.javaHashMappublicB.javaMapA.java

    // Avoid
    a.getMap().put(key,value)
    
    //recommended
    
    public void addElement(Object key, Object value){
           // Have null checks for both key and value here : single place
           map.put(key,value);
    }
    
  5. 最后,在正确的位置有效地使用积木。try{} catch{} finally{}

22赞 3 revs, 2 users 74%Raghu K Nair #47

对于 Java 8 或更高版本,最好的选择可能是使用 Optional 类。

Optional stringToUse = Optional.of("optional is there");
stringToUse.ifPresent(System.out::println);

这对于可能为 null 值的长链特别方便。例:

Optional<Integer> i = Optional.ofNullable(wsObject.getFoo())
    .map(f -> f.getBar())
    .map(b -> b.getBaz())
    .map(b -> b.getInt());

关于如何在 null 上抛出异常的示例:

Optional optionalCarNull = Optional.ofNullable(someNull);
optionalCarNull.orElseThrow(IllegalStateException::new);

Java 7 引入了 Objects.requireNonNull 方法,当应该检查某些东西是否为非空时,这种方法非常方便。例:

String lowerVal = Objects.requireNonNull(someVar, "input cannot be null or empty").toLowerCase();
5赞 2 revs, 2 users 99%Ivan Golovach #48

在 Java 8 中,如果 local-variable/field/method-argument/method-return-type 从未赋值(并且不检查 ),则可以使用 type,如果可以,则可以使用 type。然后使用处理方法和处理方法:TnullnullOptional<T>nullmapT ->flatMapT -> Optional<R>

class SomeService {
    @Inject
    private CompanyDao companyDao;

    // return Optional<String>
    public Optional<String> selectCeoCityByCompanyId0(int companyId) {
        return companyDao.selectById(companyId)
                .map(Company::getCeo)
                .flatMap(Person::getHomeAddress)
                .flatMap(Address::getCity);
    }

    // return String + default value
    public String selectCeoCityByCompanyId1(int companyId) {
        return companyDao.selectById(companyId)
                .map(Company::getCeo)
                .flatMap(Person::getHomeAddress)
                .flatMap(Address::getCity)
                .orElse("UNKNOWN");
    }

    // return String + exception
    public String selectCeoCityByCompanyId2(int companyId) throws NoSuchElementException {
        return companyDao.selectById(companyId)
                .map(Company::getCeo)
                .flatMap(Person::getHomeAddress)
                .flatMap(Address::getCity)
                .orElseThrow(NoSuchElementException::new);
    }
}

interface CompanyDao {
    // real situation: no company for such id -> use Optional<Company> 
    Optional<Company> selectById(int id);
}

class Company {
    // company always has ceo -> use Person 
    Person ceo;
    public Person getCeo() {return ceo;}
}

class Person {
    // person always has name -> use String
    String firstName;
    // person can be without address -> use Optional<Address>
    Optional<Address> homeAddress = Optional.empty();

    public String getFirstName() {return firstName;}   
    public Optional<Address> getHomeAddress() {return homeAddress;}
}

class Address {
    //  address always contains country -> use String
    String country;
    //  city field is optional -> use Optional<String>
    Optional<String> city = Optional.empty();

    String getCountry() {return country;}    
    Optional<String> getCity() {return city;}
}
-2赞 Vidura Mudalige #49

空对象模式可用作此问题的解决方案。为此,应修改 someObject 的类。

public abstract class SomeObject {
   public abstract boolean isNil();
}

public class NullObject extends SomeObject {
   @Override
   public boolean isNil() {
      return true;
   }
}
public class RealObject extends SomeObject {
   @Override
   public boolean isNil() {
      return false;
   }
}

现在检查一下,

 if (someobject != null) {
    someobject.doCalc();
}

我们可以使用,

if (!someObject.isNil()) {
   someobject.doCalc();
}

参考资料 : https://www.tutorialspoint.com/design_pattern/null_object_pattern.htm

评论

5赞 Yasin 10/26/2016
您只是在修改检查。没有得到真正的好处。尽管我们付出了额外的努力来实现建议的模式,但代码仍然不干净。
0赞 Manoel Campos 11/20/2019
如果实现 Null 对象模式,则应返回 NullObject,而不是返回 null。这样,您就不需要检查了。只需调用 .如果对象是 NullObject 实例,则不会执行任何操作。它只是该方法的空实现。object.doCalc()doCalc()
0赞 Dan Chase 10/20/2020
你知道,你基本上是用你自己的 Null 版本替换 Null,然后说使用这个版本的 Null,这样你就不必使用语言:)附带的另一个版本
7赞 Philip John #50

由于该类存在。Java 7java.util.Objects

但是,由于 ,您可以使用类的 和 方法来执行空指针检查。Java 8Objects.isNull(var)Objects.nonNull(var)Objects

例如

String var1 = null;
Date var2 = null;
Long var3 = null;

if(Objects.isNull(var1) && Objects.isNull(var2) && Objects.isNull(var3))
    System.out.println("All Null");
else if (Objects.nonNull(var1) && Objects.nonNull(var2) && Objects.nonNull(var3))
    System.out.println("All Not Null");
6赞 4 revsJobin #51

如果您使用的是 java8 或更高版本,请使用 from .isNull(yourObject)java.util.Objects

例:-

String myObject = null;

Objects.isNull(myObject); //will return true

用法:下面的代码返回一个非 null 值(如果名称不为 null,则返回该值,否则将返回默认值)。

final String name = "Jobin";
String nonNullValue = Optional.ofNullable(name).orElse("DefaultName");

评论

0赞 PeterMmm 7/7/2017
这与 Java8 中的 lambda 特性没有太大区别。myObject == nullPredicates
0赞 RealHowTo 10/12/2018
Objects.isNull() 从 Java8(不是 Java7)开始可用
0赞 J-J 10/13/2018
@RealHowTo 感谢您指出这一点,我已经更新了答案,实际上是在 java7 中引入的,稍后添加到其中。ObjectsisNull
1赞 Araxia 5/17/2022
这是 的一个糟糕的用法示例。这里是多余的。 如果是,将返回,所以你可以这样做:Objects.isNullfilterOptional.ofNullable()Optional.empty()namenullString nonNullValue = Optional.ofNullable(name).orElse("DefaultName");
12赞 2 revsUser17 #52

Java 8 在 java.util 包中引入了一个新类 Optional。

Java 8 的优点 可选:

1.) 不需要空检查。
2.) 运行时不再出现 NullPointerException。
3.) 我们可以开发干净整洁的 API。

可选 - 容器对象,该对象可能包含也可能不包含非 null 值。如果存在值,isPresent() 将返回 true,get() 将返回该值。

有关更多详细信息,请在此处找到 oracle 文档:- https://docs.oracle.com/javase/8/docs/api/java/util/Optional.html

评论

0赞 fozzybear 3/23/2023
应该注意的是,它们应该主要用于返回值,作为空对象工作而不会出现连续问题,而不是作为空检查的替代品,例如在遗留或传统代码中。OptionalsNPE
12赞 yanefedor #53

总而言之,避免陈述

if (object != null) {
    ....
}
  1. 从 Java 7 开始,您可以使用以下方法:Objects

    Objects.isNull(对象)

    Objects.nonNull(对象)

    Objects.requireNonNull(对象)

    Objects.equals(对象 1, 对象 2)

  2. 从 java 8 开始,您可以使用 Optional 类(何时使用)

object.ifPresent(obj -> ...);爪哇 8

object.ifPresentOrElse(obj -> ..., () -> ...);爪哇 9

  1. 依赖方法契约 (JSR 305) 并使用 Find Bugs。使用注释和 .此外,还提供先决条件。@javax.annotation.Nullable@javax.annotation.Nonnnul

    前提条件.checkNotNull(对象);

  2. 在特殊情况下(例如,对于字符串和集合),您可以使用 apache-commons(或 Google guava)实用程序方法:

public static boolean isEmpty(CharSequence cs) //apache CollectionUtils

public static boolean isEmpty(Collection coll) //apache StringUtils

public static boolean isEmpty(Map map) //apache MapUtils

public static boolean isNullOrEmpty(@Nullable String string) //番石榴字符串

  1. 当你需要分配默认值时,当 null 使用 apache commons lang

公共静态对象 defaultIfNull(对象,对象 defaultValue)

评论

0赞 Brijesh Mavani 1/18/2018
解释得很好
1赞 Binod Pant #54

您拥有的一种选择

  • 使用检查器框架对方法的@RequiresNonNull。例如,如果您调用一个带有 null 参数的注释方法,则会得到此结果。它会在编译过程中失败,甚至在代码运行之前!因为在运行时它将是 NullPointerException

    @RequiresNonNull(value = { "#1" })
    static void check( Boolean x) {
        if (x) System.out.println("true");
        else System.out.println("false");
    }
    
    public static void main(String[] args) {
    
    
        check(null);
    
    }
    

得到

[ERROR] found   : null
[ERROR] required: @Initialized @NonNull Boolean
[ERROR] -> [Help 1]

还有其他方法,如使用 Java 8 的 Optional、Guava Annotations、Null Object 模式等。没关系,只要你达到避免的目标!=null

3赞 nilesh #55

在 Java 8 中,您可以将供应商传递给一个帮助程序方法,如下所示:

if(CommonUtil.resolve(()-> a.b().c()).isPresent()) {

}

上面替换了下面的样板代码,

if(a!=null && a.b()!=null && a.b().c()!=null) {

}

CommonUtil.java

 public static <T> Optional<T> resolve(Supplier<T> resolver) {
        try {
            T result = resolver.get();
            return Optional.ofNullable(result);
        } catch (NullPointerException var2) {
            return Optional.empty();
        }
    }

评论

0赞 Jeff Bennett 7/22/2022
乍一看,这看起来像是解决我所遇到的问题的一个很好的答案,但它存在“不要使用异常来控制行为”的设计缺陷。从本质上讲,它之所以有效,是因为它依赖于“resolve(..)”方法,故意生成一个NPE,捕获它,然后处理它以改变行为。这太糟糕了。可能仍然使用它,但正在寻找更好的方法。
8赞 2 revsMukesh A #56

你可以通过遵循问题的大多数其他答案来避免很多,我只想添加一些在 Java 9 中引入的方法,以优雅地处理这种情况,并展示一些旧的方法也可以使用,从而减少你的工作量。NullPointerException

  1. public static boolean isNull(Object obj)

    如果提供的引用为 null,则返回 true,否则返回 假。

    从 Java 1.8 开始

  2. public static boolean nonNull(Object obj)

    如果提供的引用为非 null,则返回 true,否则返回 假。

    从 Java 1.8 开始

  3. public static <T> T requireNonNullElse​(T obj, T defaultObj)

    如果第一个参数为非 null,则返回该参数,否则返回 非 null 第二个参数。

    从 Java 9 开始

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

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

    从 Java 9 开始

  5. public static <T> T requireNonNull​(T obj, Supplier<String> messageSupplier)

    检查指定的对象引用是否不为 null,否则将引发自定义的 NullPointerException。

    从 Java 1.8 开始

有关上述功能的更多详细信息,请参见此处

评论

0赞 Ibo 7/13/2018
你可以看到有很多答案有很多赞成票,你的答案有什么特别之处?
1赞 Mukesh A 7/13/2018
我只是想添加更多可用的功能。Java 9
0赞 Ibo 7/13/2018
然后在帖子开头用粗体强调它
1赞 Francis #57

具有 null 安全性的 Kotlin 是优雅的替代方案,但这意味着更大的变化。

0赞 Ramprabhu #58

函数式方法可能有助于包装重复的 null 检查并执行匿名代码,如以下示例所示。

    BiConsumer<Object, Consumer<Object>> consumeIfPresent  = (s,f) ->{
        if(s!=null) {
            f.accept(s);
        }
    };

    consumeIfPresent.accept(null, (s)-> System.out.println(s) );
    consumeIfPresent.accept("test", (s)-> System.out.println(s));

    BiFunction<Object, Function<Object,Object>,Object> executeIfPresent  = (a,b) ->{
        if(a!=null) {
            return b.apply(a);
        }
        return null;
    };
    executeIfPresent.apply(null, (s)-> {System.out.println(s);return s;} );
    executeIfPresent.apply("test", (s)-> {System.out.println(s);return s;} );
4赞 2 revsSebastian Brandt #59

!= null 检查的另一种选择是(如果您无法在设计上摆脱它):

Optional.ofNullable(someobject).ifPresent(someobject -> someobject.doCalc());

Optional.ofNullable(someobject).ifPresent(SomeClass::doCalc);

其中 SomeClass 是某个对象的类型。

但是,您无法从 doCalc() 中获取返回值,因此仅对 void 方法有用。

评论

0赞 ThisIsNoZaku 8/6/2019
您可以通过结合使用或任何其他方法来获取 Optional 中的值,而不是 .mapgetifPresent
8赞 2 revs, 2 users 96%rohit prakash #60

Java 8 在包中引入了一个新类 Optional。它用于表示值存在或不存在。这种新结构的主要优点是不再有太多的 null 检查和 .它避免了任何运行时,并支持我们开发干净整洁的 Java API 或应用程序。与 和 一样,它也是一个最多保存一个值的容器。java.utilNullPointerExceptionNullPointerExceptionsCollectionsarrays

以下是您可以点击的一些有用链接

https://www.mkyong.com/java8/java-8-optional-in-depth/

https://dzone.com/articles/java-8-optional-avoid-null-and

0赞 Satish Hawalppagol #61

您可以为对象和字符串创建一个通用方法,以便您可以在应用程序中使用它 - 这可以帮助您和您的同事: 创建一个类,例如。StringUtilities 并添加方法,例如。getNullString

public static String getNullString(Object someobject)
{
   if(null==someobject )
        return null;

   else if(someobject.getClass().isInstance("") && 
          (((String)someobject).trim().equalsIgnoreCase("null")|| 
          ((String)someobject).trim().equalsIgnoreCase("")))
        return null;

   else if(someobject.getClass().isInstance(""))
        return (String)someobject;

   else
        return someobject.toString().trim();
}

只需将此方法称为,

if (StringUtilities.getNullString(someobject) != null)
{ 
  //Do something
}
0赞 3 revsDan Chase #62

在 Java 中避免 Null 检查的最佳方法是正确处理和使用异常。根据我的经验,随着您更接近前端,Null 检查变得越来越常见和必需,因为它更接近可能通过 UI 提供无效信息的用户(例如,没有值,正在为字段提交)。

有人可能会争辩说,您应该能够控制 UI 正在执行的操作,以免您忘记大多数 UI 是通过某种第三方库完成的,例如,它可能会返回 NULL 或空白文本框的空字符串,具体取决于情况或库。

您可以像这样将两者结合起来:

try
{
  myvar = get_user_supplied_value(); 
  if (myvar == null || myvar.length() == 0) { alert_the_user_somehow(); return; };

  process_user_input(myvar);
} catch (Exception ex) {
  handle_exception(ex);
}

人们采取的另一种方法是说:

if (myvar && myvar.length() > 0)  { };

你也可以抛出一个异常(这是我更喜欢的)

if (myvar == null || myvar.length() == 0) {
 throw new Exception("You must supply a name!");
};

但这取决于你。

评论

0赞 Brian_Entei 7/14/2021
在这种情况下,myvar 是布尔值还是字符串?我很难弄清楚你所说的“如果存在”是什么意思。这也许是其他语言中检查 null 的方式吗?!myvarmyvar.length()
1赞 Dan Chase 7/15/2021
@Brian_Entei !myvar 应该解析为 (myvar != true),并且由于 NULL 永远不等于任何内容,如果它为 NULL,它将正确解析,并且 !myvar 应该捕获它。我相信这在大多数语言中都是可行的。基本上,我正在检查以确保它不是空,如果是,则短路二次检查的长度。如果它不是 null,则检查变量的长度。在本例中,它将是一个字符串。更多的是整体空检查与异常,我没有过多关注语言。
0赞 Brian_Entei 7/15/2021
啊,我明白了。在 Java 中,尝试在原始布尔值(或具有自动拆箱功能的布尔对象)或表达式以外的任何内容上使用运算符会导致语法错误并且无法编译,所以我很困惑。感谢您的澄清。!
1赞 Dan Chase 7/16/2021
@Brian_Entei 有趣!谢谢,现在我意识到既然答案是针对 Java,我应该更加小心地坚持 Java 语法。不好意思!
3赞 2 revs, 2 users 84%Allen #63

有一种很好的方法可以检查 JDK 的 null 值。 Optional.java 拥有解决这些问题的大量方法。如如下:

    /**
     * Returns an {@code Optional} describing the specified value, if non-null,
     * otherwise returns an empty {@code Optional}.
     *
     * @param <T> the class of the value
     * @param value the possibly-null value to describe
     * @return an {@code Optional} with a present value if the specified value
     * is non-null, otherwise an empty {@code Optional}
     */
    public static <T> Optional<T> ofNullable(T value) {
        return value == null ? empty() : of(value);
    }
    /**
     * Return {@code true} if there is a value present, otherwise {@code false}.
     *
     * @return {@code true} if there is a value present, otherwise {@code false}
     */
    public boolean isPresent() {
        return value != null;
    }
    /**
     * If a value is present, invoke the specified consumer with the value,
     * otherwise do nothing.
     *
     * @param consumer block to be executed if a value is present
     * @throws NullPointerException if value is present and {@code consumer} is
     * null
     */
    public void ifPresent(Consumer<? super T> consumer) {
        if (value != null)
            consumer.accept(value);
    }

帮助 javer 真的非常有用。

3赞 vidy #64
public class Null {

public static void main(String[] args) {
    String str1 = null;
    String str2 = "";

    if(isNullOrEmpty(str1))
        System.out.println("First string is null or empty.");
    else
        System.out.println("First string is not null or empty.");

    if(isNullOrEmpty(str2))
        System.out.println("Second string is null or empty.");
    else
        System.out.println("Second string is not null or empty.");
}

public static boolean isNullOrEmpty(String str) {
    if(str != null && !str.isEmpty())
        return false;
    return true;
}
}

输出

str1 is null or empty.
str2 is null or empty.

在上面的程序中,我们有两个字符串 str1 和 str2。str1 包含 null 值,str2 为空字符串。

我们还创建了一个函数 isNullOrEmpty(),顾名思义,它检查字符串是 null 还是空。它使用 != null 和字符串的 isEmpty() 方法使用 null 检查来检查它。

简单来说,如果字符串不是 null 并且 isEmpty() 返回 false,则它不是 null 或空。否则,它是。

但是,如果字符串仅包含空格字符(空格),则上述程序不会返回空。从技术上讲,isEmpty() 看到它包含空格并返回 false。对于带空格的字符串,我们使用字符串方法 trim() 来修剪所有前导和尾随空格字符。

1赞 2 revsPremakumar Tatireddy #65

Objects.isNull(null)如果您使用的是 Java8,那么您可以尝试此代码。

如果您不使用 Java8,请尝试使用以下代码

Object ob=null;
if(ob==null){ **do something}
0赞 2 revsGreg7000 #66

就个人而言,我要么接受 jim-nelson 的答案,要么如果我发现空检查对特定上下文很方便,我会将 lombok 合并到我的项目中并使用 @NonNull 注释。

例:

import lombok.NonNull;

public class NonNullExample extends Something {
  private String name;
  
  public NonNullExample(@NonNull Person person) {
    super("Hello");
    this.name = person.getName();
  }
}

甚至@NonNull序言也提到:

  • 或者:我如何学会停止担心并喜欢 NullPointerException。
0赞 Geoffrey #67

这是一个大问题,唯一有效的答案是:您无法避免在 java 中检查 null

问题来自语言语法本身,一个对象总是可以是类型化或空的。甚至 Optional 对象也可以为 null。

所有其他建议(注释、可选等)只是围绕问题添加了更多的样板代码,但从未真正解决过它。更糟糕的是,手动处理的复杂性和极端情况会增加。

唯一可行的解决方案是不要自己编写样板,而让另一个工具为您完成。那是您使用 Kotlin 的时候。当他们设计语言时,空安全性是一个主要问题。

此外,Java 语法永远不会发生如此深刻的变化,因为它将意味着一些重大的突破性变化,实际上创造了一种新语言(kotlin 在最后已经做到了)。

这就是最近许多公司转向 kotlin 的主要原因。查看一些有关Facebook,Google android的文章...

而Java世界的一个主要问题是,很多人很久以前就学会了Java,以至于他们在情感上依恋它,不想改变他们的习惯。因此,他们试图证明他们奇怪和过时的模式是合理的,而不仅仅是使用元语言,比如前端世界的 JavaScript TypeScript。

0赞 Sahi Repswal #68

可以通过以下方式处理:

import org.apache.commons.lang.ObjectUtils;
if(!ObjectUtils.equals(x, null)){

}