8判空 java 避免!= null语句



15 Answers

如果您使用(或计划使用)Java IDE,如JetBrains IntelliJ IDEA ,Eclipse或Netbeans或像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 ,他们添加了对任何其他@Nullable @NotNull实现的支持。

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

java多线程空指针

我使用object != null来避免NullPointerException

有没有一个很好的替代品呢?

例如:

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

当不知道对象是否为null时,这可以避免NullPointerException

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




哇,当我们有57种不同的推荐NullObject pattern方法时,我几乎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和Devoxx选民都非常喜欢这个提议,但也存在反对意见,理由是它实际上会鼓励更多地使用null作为哨兵价值。

更新:已在Project Coin下提交了Java 7中关于零安全运算符的proposed 语法与上面的示例略有不同,但它是相同的概念。

更新:零安全运营商提案未进入项目硬币。 因此,您将不会在Java 7中看到此语法。




仅限于这种情况 -

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

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

如果foo不存在,将导致NullPointerException

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

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



根据您检查的对象类型,您可以使用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");



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




有时,您有一些方法可以对其参数进行操作,从而定义对称操作:

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

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




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

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



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

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



问这个问题指出你可能对错误处理策略感兴趣。您的团队的架构师应该决定如何处理错误。做这件事有很多种方法:

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

    • 检查错误情况并适当处理它们

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




只是不要使用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,你必须以某种方式弄清楚。




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

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

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

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

包装选项<T>

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

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

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

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

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

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

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

@FunctionalInterface
public interface Consumer<T>

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

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

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

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




我试过了,NullObjectPattern但对我来说并不总是最好的方式。有时候“不采取行动”是不恰当的。

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

现在回答:

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

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

字节!




我可以更一般地回答它!

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

   // ...
}



  1. 永远不要将变量初始化为null。
  2. 如果(1)不可能,则将所有集合和数组初始化为空集合/数组。

在您自己的代码中执行此操作,您可以避免!= 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来说它是值得的。




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





Related