避免!= null语句

时间:2008-11-07 08:31:40

标签: java object nullpointerexception null

我经常使用object != null来避免NullPointerException

有没有一个很好的替代方案?

例如:

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

如果不知道对象是否为NullPointerException,则会避免null

请注意,接受的答案可能已过期,请参阅https://stackoverflow.com/a/2386013/12943了解更新的方法。

67 个答案:

答案 0 :(得分:2533)

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

换句话说,有两个出现空检查的实例:

  1. 如果null是合同方面的有效回复;以及

  2. 如果它不是有效的回复。

  3. (2)很容易。使用assert语句(断言)或允许失败(例如,NullPointerException)。断言是1.4中添加的高度未充分利用的Java功能。语法是:

    assert <condition>
    

    assert <condition> : <object>
    

    其中<condition>是布尔表达式,<object>是一个对象,其toString()方法的输出将包含在错误中。

    如果条件不为真,assert语句将抛出ErrorAssertionError)。默认情况下,Java会忽略断言。您可以通过将选项-ea传递给JVM来启用断言。您可以为各个类和包启用和禁用断言。这意味着您可以在开发和测试时使用断言验证代码,并在生产环境中禁用它们,尽管我的测试表明,断言没有性能影响。

    在这种情况下不使用断言是正常的,因为代码只会失败,如果使用断言将会发生这种情况。唯一的区别在于,断言可能会更快地发生,以更有意义的方式发生,并且可能带有额外的信息,这可能有助于您弄清楚如果您没有预料到它会发生的原因。

    (1)有点难。如果您无法控制您正在调用的代码,那么您就会陷入困境。如果null是有效响应,则必须检查它。

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

    对于非收藏品,它可能更难。以此为例:如果您有这些接口:

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

    Parser接受原始用户输入并找到要做的事情,也许是在为某些事情实现命令行界面时。现在,如果没有适当的操作,您可以使合同返回null。这导致你正在谈论的空检查。

    另一种解决方案是永远不会返回null,而是使用Null Object pattern

    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()方法抛出一个带有意义错误消息的异常是完全合适的 - 特别是在这种情况下你依赖于用户输入。对于findAction方法来说,抛出一个Exception比使用一个简单的NullPointerException而没有解释的调用方法更好。

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

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

    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);
            }
        }
    }
    

答案 1 :(得分:578)

如果您使用(或计划使用)诸如JetBrains IntelliJ IDEAEclipseNetbeans之类的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实施的支持。

请参阅博文 More flexible and configurable @Nullable/@NotNull annotations

答案 2 :(得分:303)

如果不允许空值

如果您的方法是从外部调用的,请从以下内容开始:

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

然后,在该方法的其余部分中,您将知道object不为空。

如果它是一个内部方法(不是API的一部分),只需记录它不能为空,就是这样。

示例:

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

但是,如果你的方法只是传递了值,并且下一个方法将它传递给等等,那么它可能会出现问题。在这种情况下,您可能需要检查上面的参数。

如果允许null

这实际上取决于。如果发现我经常做这样的事情:

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

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


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

如果您举例说明您通常使用这个习语的例子,可能会更容易给您举例。

答案 3 :(得分:227)

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

Alex Miller给出的例子如下:

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

?.表示只取消引用左侧标识符(如果它不为空),否则将表达式的其余部分计算为null。有些人,比如Java Posse成员Dick Wall和voters at Devoxx真的很喜欢这个提议,但也存在反对意见,理由是它实际上鼓励更多地使用null作为哨兵价值。


更新:已在official proposal下提交了Java 7中零安全运算符的Project Coin.语法与上面的示例略有不同,但它是同样的想法。


更新: null-safe运营商提案未将其纳入Project Coin。因此,您将不会在Java 7中看到这种语法。

答案 4 :(得分:187)

如果不允许未定义的值:

您可以配置IDE以警告您可能出现空取消引用。例如。在Eclipse中,请参阅首选项&gt; Java&gt;编译器&gt;错误/警告/空分析

如果允许未定义的值:

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

  • 在API中明确说明输入或输出是否存在。
  • 编译器强制您处理“未定义”的情况。
  • Option is a monad,因此无需进行详细的空检查,只需使用map / foreach / getOrElse或类似的组合来安全地使用值(example)

Java 8有一个内置的Optional类(推荐);对于早期版本,有库替代品,例如GuavaOptionalFunctionalJavaOption。但是,与许多功能样式模式一样,在Java中使用Option(甚至8)会产生相当多的样板,使用较简洁的JVM语言可以减少这种样板,例如: Scala或Xtend。

如果你必须处理可能返回null的API ,那么你在Java中做不了多少。 Xtend和Groovy有Elvis operator ?:null-safe dereference operator ?.,但请注意,如果是空引用,则返回null,因此它只是“延迟”正确的处理null。

答案 5 :(得分:174)

仅适用于这种情况 -

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

if ( foo.equals("bar") ) {
 // ...
}
如果NullPointerException不存在,

将导致foo

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

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

答案 6 :(得分:154)

使用Java 8,新的java.util.Optional类可以解决一些问题。至少可以说它提高了代码的可读性,在公共API的情况下,使API的合同更清晰。

他们的工作方式如下:

创建给定类型(Fruit)的可选对象作为方法的返回类型。它可以为空或包含Fruit对象:

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值仍然是必要的,但至少开发人员意识到该值可能为空并且忘记检查的风险有限。

在使用Optional从头构建的API中,只要返回值可能为空,并且仅在不能为null(约定)时返回普通对象,客户端代码可能会放弃空检查简单对象返回值...

当然Optional也可以用作方法参数,在某些情况下,这可能是指示可选参数而不是5或10重载方法的更好方法。

Optional提供了其他方便的方法,例如允许使用默认值的orElse,以及与lambda expressions一起使用的ifPresent

我邀请您阅读本文(我写这个答案的主要来源),其中NullPointerException(和一般的空指针)有问题以及Optional带来的(部分)解决方案很好地解释了: Java Optional Objects

答案 7 :(得分:121)

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

示例:

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

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

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

StringUtils类只是其中之一;在公共场所中有很多优秀的类可以安全操作。

下面是一个如何在包含apache库(commons-lang-2.4.jar)时在JAVA中使用null vallidation的示例

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使用这个静态classf的例子(org.springframework.util.Assert)

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

答案 8 :(得分:93)

  • 如果您认为某个对象不应为null(或者它是一个错误),请使用断言。
  • 如果你的方法不接受null params在javadoc中说它并使用断言。

只有当你想要处理对象可能为null的情况时,你必须检查对象!= null ...

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

答案 9 :(得分:88)

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

答案 10 :(得分:74)

Google集合框架提供了一种实现空检查的优雅方式。

像这样的库类中有一个方法:

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();

答案 11 :(得分:73)

有时,您可以使用定义对称操作的参数进行操作:

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

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

答案 12 :(得分:71)

而不是Null对象模式 - 它有其用途 - 你可能会考虑null对象是一个bug的情况。

抛出异常时,检查堆栈跟踪并完成错误。

答案 13 :(得分:68)

Null不是'问题'。它是complete建模工具集的组成部分。软件旨在模拟世界的复杂性,而null承担其负担。 Null表示Java等中的“无数据”或“未知”。因此,为这些目的使用null是合适的。我不喜欢'Null对象'模式;我认为它引发了“who will guard the guardians”问题。
如果你问我女朋友的名字我会告诉你我没有女朋友。在Java语言中,我将返回null。 另一种方法是抛出有意义的异常来指示一些不能(或不希望)在那里解决的问题并将其委托给堆栈中更高的位置来重试或向用户报告数据访问错误。

  1. 对于“未知问题”,请提供“未知答案”。(从业务角度来看,这是正确的,请保持无效)在使用前在方法内检查一次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);
    

    并且不要厌恶键入<alt> + <shift> + <j>(在Eclipse中生成javadoc)并为您的公共API写下三个额外的单词。除了那些不阅读文档的人之外,这对所有人来说都足够了。

    /**
     * @return photo or null
     */
    

    /**
     * @return photo, never null
     */
    
  2. 这是理论上的情况,在大多数情况下,您应该更喜欢java null safe API(如果它将在未来10年内发布),但NullPointerException是{Exception的子类1}}。因此,它是Throwable的一种形式,表示合理的应用程序可能想要捕获的条件(javadoc)!要使用“常规”代码(according to creators of Java)中的第一个最大优势和单独的错误处理代码,我应该抓住NullPointerException

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

    可能会出现问题:

    Q值。如果getPhotoDataSource()返回null,该怎么办? 答:这取决于业务逻辑。如果我找不到相册,我会告诉你没有照片。如果未初始化appContext怎么办?这种方法的业务逻辑就是这样。如果相同的逻辑应该更严格,那么抛出异常它就是业务逻辑的一部分,应该使用null的显式检查(情况3)。 新的Java Null-safe API在这里更适合指定有意义的含义和不暗示初始化的内容在程序员错误的情况下快速失败。

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

    Q值。由于尝试关闭开口,这种方法具有性能损失 答:软件应该易于理解和修改。只有在此之后,才能考虑性能,并且只在需要时才考虑!并在需要的地方! (source)和其他许多人。)

    PS。这种方法使用合理,因为单独的错误处理代码来自“常规”代码原则在某些地方使用是合理的。考虑下一个例子:

    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;
    }
    

    PPS。对于那些快速下载(而不是那么快阅读文档)的人,我想说我的生活中从未发现空指针异常(NPE)。但是这种可能性是由Java创建者故意设计的,因为NPE是Exception的子类。当ThreadDeathError时,我们在Java历史上有一个先例,不是因为它实际上是一个应用程序错误,而仅仅是因为它不打算被抓住! NPE比Error多适合ThreadDeath!但事实并非如此。

  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问题,这实际上是应用程序错误。程序员应该在初始化的东西中修复bug。

答案 14 :(得分:68)

Java 7有一个新的java.util.Objects实用程序类,其中有requireNonNull()方法。如果它的参数为null,那么所有这一切都抛出一个NullPointerException,但它会稍微清理一下代码。例如:

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

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

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

变为

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

答案 15 :(得分:50)

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

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

答案 16 :(得分:35)

Java中常见的“问题”。

首先,我对此的看法:

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

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

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

首先,我遵循这个惯例:

  1. 所有公共方法/ API始终检查其参数为null
  2. 所有私有方法都不检查null,因为它们是受控方法(只是在上面没有处理的情况下使用null指针异常)
  3. 唯一不检查null的其他方法是实用程序方法。它们是公开的,但是如果你出于某种原因打电话给它们,你就知道你传递了什么参数。这就像试图在没有提供水的情况下在水壶中煮沸水......
  4. 最后,在代码中,公共方法的第一行是这样的:

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

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

    如果任何参数为null,则方法validate()将抛出已检查的ValidationException(选中或取消选中更多是设计/品味问题,但检查了我的ValidationException

    void validate() throws ValidationException;
    

    如果“计划”为空,则该消息将包含以下文本:

    参数[计划]

    遇到非法参数值null

    正如您所看到的,用户消息需要addParam()方法(字符串)中的第二个值,因为您无法轻松检测传入的变量名称,即使使用反射(无论如何都不是此帖子的主题.. )。

    是的,我们知道超出这一行我们将不再遇到空值,所以我们只是安全地调用这些对象上的方法。

    这样,代码干净,易于维护和读取。

答案 17 :(得分:33)

询问该问题指出您可能对错误处理策略感兴趣。您的团队的架构师应该决定如何处理错误。有几种方法可以做到这一点:

  1. 允许异常通过 - 在'主循环'或其他一些管理例程中捕获它们。

    • 检查错误情况并妥善处理
  2. 当然也要看看面向方面编程 - 他们有很好的方法将if( o == null ) handleNull()插入到你的字节码中。

答案 18 :(得分:33)

除了使用assert之外,您还可以使用以下内容:

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

这稍微好于:

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



    .....
}

答案 19 :(得分:31)

只是不要使用null。不要允许它。

在我的类中,大多数字段和局部变量都具有非空的默认值,并且我在代码中的任何位置添加了契约语句(always-on asserts)以确保执行此操作(因为它更简洁,更具表现力)而不是让它作为一个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,你必须以某种方式弄明白。

答案 20 :(得分:29)

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

如维基中所述:

  

Optional<T>是用a替换可空的T引用的一种方法   非空值。 Optional可以包含非null T引用   (在这种情况下,我们说引用是“存在”),或者它可能包含   什么都没有(在这种情况下我们说引用是“缺席”)。永远不会   说“包含空”。

用法:

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

答案 21 :(得分:22)

对于每个Java开发人员来说,这是一个非常常见的问题。因此,Java 8中有官方支持来解决这些问题而不会出现混乱的代码。

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

简而言之,Optional类包括显式处理值存在或不存在的情况的方法。然而,与空引用相比的优点是Optional&lt; T&gt;。 class强制您在值不存在时考虑该情况。因此,您可以防止意外的空指针异常。

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

包装选项&lt; T&gt;

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

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

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

那么它是如何帮助避免空检查的?

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

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

Optional.ifPresent如果它是非空值,则使用引用调用给定的Consumer。否则,它什么都不做。

@FunctionalInterface
public interface Consumer<T>

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

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

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

现在HomeServices.get()做同样的事情,但是以更好的方式。它检查服务是否已初始化。如果是,则返回相同或创建新的新服务。可选&lt; T&gt; .orElse(T)有助于返回默认值。

最后,这是我们的NPE以及无空检查代码:

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 as well as Null check-free code … Really?

答案 22 :(得分:21)

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

在文章中还有一个指向Java Maybe Type的Git存储库的链接,我发现它很有趣,但我不认为它可以减少 检查代码臃肿。在对互联网进行一些研究之后,我认为!= null 代码膨胀可能主要通过精心设计来减少。

答案 23 :(得分:19)

我尝试了NullObjectPattern,但对我来说并不总是最好的方式。有时候“没有行动”是不合适的。

NullPointerException是一个运行时异常,这意味着它是开发人员的错误,并且有足够的经验告诉您确切的错误位置。

现在回答:

尽量使所有属性及其访问者尽可能保密,或者避免将它们暴露给客户端。当然,您可以在构造函数中包含参数值,但是通过减小范围,您不会让客户端类传递无效值。如果您需要修改值,则始终可以创建新的object。您只检查构造函数中的值一次,在其他方法中,您几乎可以确定这些值不为空。

当然,经验是了解和应用此建议的更好方式。

字节!

答案 24 :(得分:16)

Java 8或更高版本的最佳替代方法可能是使用Optional类。

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

对于可能为空的值的长链,这尤其方便。例如:

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方法,当检查某些内容是否为非null时,该方法很方便。例如:

String lowerVal = Objects.requireNonNull(someVar, "input cannot be null or empty").toLowerCase();

答案 25 :(得分:15)

我可以更广泛地回答一下!

当方法以我们不期望的方式获取参数时,我们通常面临这个问题(错误的方法调用是程序员的错误)。例如:您希望获得一个对象,而不是获得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)

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

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

public class Car {

   @NotNull
   private String manufacturer;

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

   @Min(2)
   private int seatCount;

   // ...
}

答案 26 :(得分:15)

我高度忽略了建议在每种情况下使用null对象的答案。这种模式可能会破坏合同并将问题埋得越来越深,而不是解决它们,没有提到使用不当会产生另一堆样板代码,需要将来维护。

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

另外请记住,如果毫无使用,那么空对象模式将会耗尽内存。为此 - 应该在所有者之间共享NullObject的实例,而不是每个实例的单独实例。

此外,我不建议使用此模式,其中类型是基本类型表示 - 如数学实体,不是标量:向量,矩阵,复数和POD(普通旧数据)对象,这意味着以Java内置类型的形式保存状态。在后一种情况下,您最终会调用具有任意结果的getter方法。例如,NullPerson.getName()方法应返回什么?

值得考虑这些案例,以避免荒谬的结果。

答案 27 :(得分:14)

  1. 永远不要将变量初始化为null。
  2. 如果(1)不可用,请将所有集合和数组初始化为空集合/数组。
  3. 在您自己的代码中执行此操作,您可以避免!= 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来说它是值得的。

答案 28 :(得分:14)

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

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

方法1:

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

notNull(Object object,String message)

方法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;
}

答案 29 :(得分:11)

public static <T> T ifNull(T toCheck, T ifNull) {
    if (toCheck == null) {
           return ifNull;
    }
    return toCheck;
}

答案 30 :(得分:9)

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

Java 8的优点可选:

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

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

有关详细信息,请访问此处oracle docs: - https://docs.oracle.com/javase/8/docs/api/java/util/Optional.html

答案 31 :(得分:8)

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

答案 32 :(得分:7)

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

但是从Java 8开始,您可以使用Objects.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");

答案 33 :(得分:7)

我遵循以下指南以避免空检查。

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

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

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

  4. 尽可能限制数据的 变异 。某些变量可以在构造函数中创建,永远不会更改。 删除公共setter方法,除非确实需要

    E.g。假设应用程序中的一个类(A.java)正在维护像HashMap这样的集合。不要在A.java中提供public getter方法,并允许B.java直接在Map中添加元素。而是在A.java中提供API,它将一个元素添加到集合中。

    // 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{}块。

答案 34 :(得分:7)

总而言之,以避免陈述

if (object != null) {
    ....
}
  1. 因为java 7你可以使用Objects方法:

    Objects.isNull(对象)

    Objects.nonNull(对象)

    Objects.requireNonNull(对象)

    Objects.equals(object1,object2)

  2. 因为java 8你可以使用Optional class(when to use

  3. object.ifPresent(obj -> ...); java 8

    object.ifPresentOrElse(obj -> ..., () -> ...); java 9

    1. 依赖方法合约(JSR 305)并使用Find Bugs。使用注释@javax.annotation.Nullable@javax.annotation.Nonnnul标记您的代码。还有先决条件。

      Preconditions.checkNotNull(对象);

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

    3.   

      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)// Guava Strings

      1. 当您需要在null时使用apache commons lang
      2. 分配默认值
          

        public static Object defaultIfNull(Object object,Object defaultValue)

答案 35 :(得分:6)

还有一个选择:

以下简单函数有助于隐藏空检查(我不知道原因,但我没有将其作为同一通用库的一部分):

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

你现在可以写

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

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

答案 36 :(得分:5)

仅通过关注问题的其他大多数答案,您就可以避免很多事情,从而避免NullPointerException,我只想添加一些{strong}中引入的更多方法< / strong>优雅地处理此情况,并展示一些较旧的情况也可以使用,从而减少了工作量。

  1. Java 9

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

    自Java 1.8起

  2. public static boolean isNull(Object obj)

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

    自Java 1.8起

  3. public static boolean nonNull(Object obj)

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

    自Java 9起

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

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

    自Java 9起

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

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

    自Java 1.8起

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

答案 37 :(得分:5)

我发现Guava Preconditions在这种情况下非常有用。我不喜欢将空值留给空指针异常,因为了解NPE的唯一方法是找到行号。生产版本和开发版本中的行号可能不同。

使用Guava Preconditions,我可以检查空参数并在一行中定义一个有意义的异常消息。

例如,

Preconditions.checkNotNull(paramVal, "Method foo received null paramVal");

答案 38 :(得分:5)

无论您在何处传递数组或Vector,都要将这些数据初始化为空数,而不是null。 - 通过这种方式,您可以避免大量检查null并且一切都很好:)

public class NonNullThing {

   Vector vectorField = new Vector();

   int[] arrayField = new int[0];

   public NonNullThing() {

      // etc

   }

}

答案 39 :(得分:4)

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

实施例: -

String myObject = null;

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

答案 40 :(得分:4)

您可以在方法调用之前使用拦截器。这就是aspect-oriented programming关注的重点。

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

答案 41 :(得分:4)

在Java 8中,如果从未分配T,则可以对local-variable / field / method-argument / method-return-type使用类型null(并且不检查null }}或键入Optional<T>,如果它可以是null。然后使用方法map处理T ->和方法flatMap以处理T -> 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;}
}

答案 42 :(得分:3)

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

答案 43 :(得分:3)

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

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

答案 44 :(得分:2)

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包含空值,str2是一个空字符串。

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

简而言之,如果字符串不是null且isEmpty()返回false,则它既不是null也不为空。否则。

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

答案 45 :(得分:2)

避免不必要的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. 清洁代码。能够推断一段代码行为的最重要的事情是它是用易于理解的事物编写的。根据代码的含义为变量命名,在他们执行操作后为您的方法命名,应用Single responsibility principleS SOLIDhttp://en.wikipedia.org/wiki/SOLID_(object-oriented_design),这意味着每一个代码应该只有一个责任,并且执行此操作而不执行任何其他操作。一旦您的代码干净,就可以更容易地对其进行推理,也可以跨多个层/代码层进行推理。使用凌乱的代码,尝试理解方法的作用可能会让您忘记为什么要首先阅读该方法。 (提示:阅读Robert C. Martin的“清洁代码”)

  2. 避免返回null值。如果null值会使您的程序无法正常运行,请改为抛出exception(确保添加适当的错误处理。)返回null值的情况可能是可接受的例如,尝试从数据库中获取对象。在这些情况下,编写处理null值的代码,并在您的耳后留言,我们可能会返回null。处理返回null值尽可能接近返回null的方法的调用者(不要盲目地将其传递回调用链。)

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

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

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

  6. 使用数据库时,请使用not null - 约束。通过这种方式,您将知道从数据库读取的值不能为空,您不必检查它。

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

  8. 如果您还没有这样做,请编写代码的自动化测试。通过编写测试,您将对您的代码进行推理,并且您也会更加自信地完成它应该执行的操作。此外,自动化测试可以防止您在重构过程中出现错误,让您立即知道这段代码没有像以前那样做。

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

    public String nullSafeToString(final Object o) {
        return o != null ? o.toString() : "null";
    }
    

答案 46 :(得分:2)

使用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();
        }
    }

答案 47 :(得分:2)

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

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

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

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

不要轻易实例化,认为每个“新”都会让你遇到很多麻烦并遵循这些简单的规则。

一个班级只能以三种方式访问​​记忆 - &gt;

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

    1. 所有“HAS”成员在构造函数中创建为“new”。
    2. 您将在析构函数中关闭/取消分配或等效close() 在Java中为同一个类而在其他类中运行。
  2. 这意味着您需要记住(就像Java一样)谁是每个资源的所有者或父母,并尊重所有权。对象仅由创建它的类删除。另外 - &gt;

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

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

    3. 最后,为了使所有这些工作,您需要有一个规范的设计,其中包含层次结构形式的类,并且不需要循环。

      在这种设计下,遵循上述规则,层次结构设计中的子类无法访问被销毁的指针,因为这意味着父级在子级之前被销毁,这种级别是非循环的设计不允许它。

      最后,还记得在启动系统时,您应该从层次结构的顶部到底部构建并从底部到顶部进行销毁。你永远不会有任何空指针,或者有人违反规则。

答案 48 :(得分:2)

首先,我们无法真正删除所有空条件。我们可以使用@NotNull@Nullable注释(已提及)来减少它们。但这需要得到一些框架的支持。这是OVal可以提供帮助的地方。

基本思想是object / parameters / constructor应始终满足前提条件。您可以拥有许多前提条件,例如NullableNotNull,OVal会在调用时注意对象应该处于一致状态。

我想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);

答案 49 :(得分:1)

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

R=[1,2,3]

(这个答案首先发布here。)

答案 50 :(得分:1)

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

ObjectUtils.equals(object, null)

CollectionUtils.isEmpty(myCollection);

StringUtils.isEmpty("string");

作为一种惯例,我喜欢之前的答案,为集合提供初始默认值或空集以最小化需求。

这些可以是简单的用法,使您不会遇到NullPointerException或使用空集合。这不回答关于如何处理null对象的问题,但是这些提供了对对象或集合的基本验证的一些检查。

希望这会有所帮助。

答案 51 :(得分:1)

有一个很好的方法来检查JDK中的空值。 它是Optional.java,具有大量解决这些问题的方法。如如下: `

  • 返回描述指定值的{@code Optional},如果非null,
    • 否则将返回一个空的{@code Optional}。
    • @param值的类别
    • @param值描述的可能为空的值
    • @返回带有当前值的{@code Optional}(如果指定了值)
    • 非空,否则为空{@code可选}
public static <T> Optional<T> ofNullable(T value) {
    return value == null ? empty() : of(value);
}  `

`/

  • 如果存在值,则返回{@code true},否则返回{@code false}。 *

    • @return {@code true}(如果存在值),否则{@code false} * / 公共布尔isPresent(){ 返回值!= null; }

    / **

    • 如果存在值,则使用该值调用指定的使用者,
    • 否则什么也不做。
    • @param使用者块(如果存在值)将执行
    • @抛出NullPointerException如果值存在并且{@code消费者}是
    • 空 * / 公共无效ifPresent(Consumer <?super T>消费者){ 如果(值!=空) Consumer.accept(value); }`

帮助贾弗真的非常有用。

答案 52 :(得分:1)

Java 8在java.util包中引入了新的Optional类。它用来表示存在或不存在的值。这种新构造的主要优点是,没有太多的空检查和NullPointerException。它避免了任何运行时NullPointerExceptions,并支持我们开发整洁的Java API或应用程序。像集合和数组一样,它也是一个最多容纳一个值的容器。

下面是一些您可以关注的有用链接

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

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

答案 53 :(得分:1)

我更喜欢这个

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

当然,在我的示例中,SomeObject正常处理null参数。例如,记录此类事件,不做任何其他事情。

答案 54 :(得分:0)

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

在所有其他情况下,您可能没有必要。尽可能使用封装,从而减少您想要检查null的位置。

答案 55 :(得分:0)

你有一个选择

  • 在方法上使用checker framework的@RequiresNonNull。对于ex,如果你使用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的可选,番石榴注释,空对象模式等。只要你获得避免的目标就无所谓了!= null

答案 56 :(得分:0)

Kotlin(具有null安全性)是一种不错的选择,但这意味着更大的变化。

答案 57 :(得分:0)

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

    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;} );

答案 58 :(得分:0)

!= null检查的另一种替代方法是(如果无法从设计角度摆脱它):

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

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

SomeClass是对象的类型。

尽管您无法从doCalc()返回返回值,所以仅对void方法有用。

答案 59 :(得分:0)

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

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

答案 60 :(得分:0)

您可以为对象和字符串创建一个通用方法,以便在整个应用程序中使用它- 这可以帮助您和您的同事: 创建一个类,例如。 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
}

答案 61 :(得分:0)

在Java中避免空检查的最好方法是正确处理和使用异常。随着您离前端越近,我的经验中的空检查已变得越来越普遍和必需,因为它离可能通过UI提供无效信息(例如,没有任何值,正在为字段提交)的用户越来越近。 / p>

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

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

try
{
  myvar = get_user_supplied_value(); 
  if (!myvar || 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 || myvar.length() == 0) {
 throw new Exception("You must supply a name!");
};

但这取决于您。

答案 62 :(得分:0)

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

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

现在不用检查,

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

我们可以使用,

{{1}}

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

答案 63 :(得分:-1)

这是我的方法..

class MyObjectHandler
{
public static final int EXCEPTION = (-3);
public static final int INACCESSIBLE = (-2);

public static int doSomething (MyObject obj, MyObjectParameter [] input)
{
    int returnValue= 0;

    try
    {
        if (obj != null)
        {
            returnValue = obj.doSomething(input);
        }
        else
        {
            returnValue = MyObjectHandler.INACCESSIBLE;
        }
    }
    catch (Exception e)
    {
        e.printStack();
        returnValue = MyObjectHandler.EXCEPTION;
    }
    finally
    {
        return returnValue;
    }
}

..

}

然后你的代码就像:

import xx.xx.xx.MyObjectHandler;
import xx.xx.xx.MyObjectParameter;

class Test
{

    public static void main ()
    {

        MyObject obj = null;

        MyObjectHandler.doSomething(obj, null);

    }

    ..

}

答案 64 :(得分:-2)

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

例如,代替返回字符串的函数在出现问题时返回null(比如将数字转换为字符串),让它们返回一个空字符串(&#34;&#34;)。在继续之前,您仍然必须测试返回值,但是没有特殊情况下的例外情况。这种编程风格的另一个好处是,您的程序将能够在正常操作和异常之间进行区分和相应的响应。

答案 65 :(得分:-2)

下载Groovy,设置Groovy,下载Groovy Eclipse插件,并将file.java更改为file.groovy。然后:

     String u;
     if (u) {
     } //Cast auto to false,
     else {
     }

就像PHP / JavaScript一样。然后继续使用常规Java编程,它是Java ++。

答案 66 :(得分:-10)

使用

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

它不会抛出空指针异常