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

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

当前回答

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

正如wiki中所解释的:

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

用法:

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

其他回答

如果不允许未定义的值:

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

如果允许未定义的值:

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

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

Java 8内置了可选类(推荐);对于早期版本,有一些库选项,例如Guava的Optional或FunctionalJava的Option。但是,像许多函数样式模式一样,在Java中使用Option(甚至是8)会产生一些样板,您可以使用不那么冗长的JVM语言(例如Scala或Xtend)来减少这些样板。

如果必须处理可能返回null的API,那么在Java中就做不了什么了。Xtend和Groovy有Elvis运算符?:和空安全解引用运算符?。,但请注意,如果引用为null,则返回null,因此它只是“延迟”了对null的正确处理。

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

实际上,如果从方法返回的某个值可以为空,并且调用代码必须对此做出决定,那么应该有一个更早的调用来确保状态。

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

此外,我不建议在类型是原始类型表示的情况下使用这种模式,比如数学实体,它们不是标量:向量、矩阵、复数和POD(普通旧数据)对象,它们是用来以Java内置类型的形式保存状态的。在后一种情况下,您将以任意结果调用getter方法。例如,NullPerson.getName()方法应该返回什么?

为了避免荒谬的结果,值得考虑这样的案例。

就我个人而言,我要么同意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的。

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

Java 8引入了Java.util.Optional<T>。它是一个可以容纳或不容纳非空值的容器。Java8提供了一种更安全的方法来处理在某些情况下值可能为空的对象。它的灵感来自Haskell和Scala的想法。

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

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

包装到选项<T>

让我们考虑一种从工厂获取服务引用的方法。与其返回服务引用,不如用Optional包装它。它让API用户知道返回的服务可能不可用/不起作用,可以防御性地使用

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

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

那么,如何避免空检查呢?

包装引用对象后,Optional提供了许多有用的方法来调用包装引用上的方法,而无需NPE。

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

Optional.ifRepresent调用具有引用的给定Consumer(如果它是非空值)。否则,它什么也不做。

@FunctionalInterface
public interface Consumer<T>

表示接受单个输入参数但不返回结果的操作。与大多数其他功能界面不同,Consumer预期通过副作用进行操作。它很干净,很容易理解。在上面的代码示例中,如果Optional保持引用为非空,则调用HomeService.switchOn(Service)。

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

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

现在HomeServices.get()做了同样的事情,但方式更好。它检查服务是否已初始化。如果是,则返回相同的或创建新的new服务。可选<T>。或Else(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以及空检查免费代码…真的吗?。

如果您使用(或计划使用)JetBrains IntelliJ IDEA、Eclipse或Netbeans等Java IDE或findbugs等工具,那么您可以使用注释来解决这个问题。

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

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

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

or

@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注释。