4279

我用object != null很多来避免NullPointerException

什么是替代方案:

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

65 回答 65

2785

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

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

  1. 如果 null 是合同条款中的有效响应;和

  2. 如果它不是有效的响应。

(2) 容易。使用assert语句(断言)或允许失败(例如, NullPointerException)。断言是在 1.4 中添加的一个未被充分利用的 Java 特性。语法是:

assert <condition>

或者

assert <condition> : <object>

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

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

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

(1) 有点难。如果您无法控制所调用的代码,那么您将陷入困境。如果 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() 方法抛出一个带有有意义的错误消息的异常可能是完全合适的——尤其是在这种依赖用户输入的情况下。findAction 方法抛出一个异常要比调用方法用一个简单的 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);
        }
    }
}
于 2008-11-07T12:06:40.507 回答
691

如果您使用(或计划使用)Java IDE(如JetBrains IntelliJ IDEAEclipseNetbeans )或 findbugs 等工具,则可以使用注释来解决此问题。

基本上,你有@Nullable@NotNull

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

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

于 2010-03-05T10:31:19.393 回答
346

如果不允许空值

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

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 && ...”。

如果您展示了您通常在哪里使用该成语的示例,那么给您提供示例可能会更容易。

于 2008-11-07T09:27:00.250 回答
249

哇,当我们有 57 种不同的方式来推荐NullObject pattern.处理" — if-not-equal-null 逻辑的简化语法。

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

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

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


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


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

于 2009-01-17T05:08:35.463 回答
207

如果不允许未定义的值:

您可以配置您的 IDE 以警告您潜在的 null 取消引用。例如,在 Eclipse 中,请参阅Preferences > Java > Compiler > Errors/Warnings/Null analysis

如果允许未定义的值:

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

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

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

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

于 2010-01-14T13:45:06.277 回答
204

只针对这种情况——

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

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

将导致NullPointerExceptioniffoo不存在。

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

if ( "bar".equals(foo) ) {
 // ...
}
于 2008-11-09T12:24:58.347 回答
179

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( fruits) 列表中搜索给定的 Fruit 实例:

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

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

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

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

Optional提供其他方便的方法,例如orElse允许使用默认值,以及ifPresentlambda 表达式一起使用。

我邀请您阅读这篇文章(我编写此答案的主要来源),其中很好地解释NullPointerException了(通常是空指针)问题以及带来的(部分)解决方案OptionalJava Optional Objects

于 2013-04-25T15:22:50.317 回答
130

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

例子:

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

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

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

StringUtils 类只是众多类之一;公共领域中有很多很好的类可以进行空安全操作。

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

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");
于 2008-11-07T09:10:19.657 回答
102
  • 如果您认为一个对象不应该为空(或者它是一个错误),请使用断言。
  • 如果您的方法不接受空参数,请在 javadoc 中说明并使用断言。

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

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

于 2008-11-07T08:55:26.053 回答
93

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

于 2011-05-23T18:18:14.057 回答
81

而不是空对象模式——它有它的用途——你可能会考虑空对象是一个错误的情况。

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

于 2008-11-07T08:50:24.503 回答
80

有时,您有对其参数进行操作的方法,这些参数定义了对称操作:

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

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

于 2008-11-07T09:04:11.573 回答
79

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();
于 2008-12-29T13:50:07.533 回答
75

Null 不是“问题”。它是一个完整的建模工具集的一个组成部分。软件旨在模拟世界的复杂性,而 null 则承担着它的负担。Null在 Java 等中表示“无数据”或“未知” 。因此,为这些目的使用空值是合适的。我不喜欢“空对象”模式;我认为这引发了“谁来守护监护人”的问题。
如果你问我女朋友叫什么名字,我会告诉你我没有女朋友。在 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 安全 API(以防它会在另外 10 年内发布),但NullPointerException它是Exception. 因此,它是一种Throwable表示合理应用程序可能想要捕获的条件的形式(javadoc)!要使用异常的第一个最大优势并将错误处理代码与“常规”代码分开(根据 Java 的创建者),对我来说,catch 是合适的NullPointerException

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

    可能会出现以下问题:

    问:如果getPhotoDataSource()返回 null 怎么办?
    A. 这取决于业务逻辑。如果我找不到相册,我不会给你看照片。如果 appContext 没有初始化怎么办?该方法的业务逻辑支持这一点。如果相同的逻辑应该更严格,那么抛出异常是业务逻辑的一部分,应该使用显式检查 null (案例 3)。新的Java Null-safe API 更适合这里有选择地指定什么暗示和不暗示在程序员错误的情况下被初始化为快速失败。

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

    问:由于 try 闭包打开,这种方法会降低性能。
    A. 软件首先应该易于理解和修改。只有在此之后,才可以考虑性能,并且仅在需要时才考虑!和需要的地方!(来源),以及许多其他)。

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

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

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

于 2011-11-21T12:58:18.847 回答
71

Java 7 有一个新的java.util.Objects实用程序类,其中有一个requireNonNull()方法。如果它的参数为空,所有这些都会抛出一个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");
}
于 2012-08-10T07:08:00.533 回答
52

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

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

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

首先,我对此的看法:

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

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

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

首先,我遵循这个约定:

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

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

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

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

如果任何参数为空,方法validate()将抛出已检查(选中或未选中更多是设计/品味问题,但我已选中)。ValidationExceptionValidationException

void validate() throws ValidationException;

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

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

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

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

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

于 2011-03-15T12:42:25.210 回答
38

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

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

当然也可以看看 Aspect Oriented Programming - 他们有巧妙的方法插入if( o == null ) handleNull()你的字节码。

于 2008-11-07T09:27:17.813 回答
37

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

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

这略好于:

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



    .....
}
于 2010-08-17T03:59:45.513 回答
36

只是永远不要使用空值。不允许。

在我的类中,大多数字段和局部变量都具有非空默认值,并且我在代码中的任何地方都添加了合同声明(始终开启断言)以确保执行此操作(因为它比让它更简洁、更具表现力)作为 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。

于 2012-10-18T03:42:36.160 回答
31

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

正如维基中解释的那样:

Optional<T>是一种用非空值替换可为空的 T 引用的方法。Optional 可能包含非空 T 引用(在这种情况下,我们说引用是“存在的”),或者它可能不包含任何内容(在这种情况下,我们说引用是“不存在的”)。从来没有说它“包含空值”。

用法:

Optional<Integer> possible = Optional.of(5);
possible.isPresent(); // returns true
possible.get(); // returns 5
于 2012-03-23T20:16:39.240 回答
27

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

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

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

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

包装到选项<T>

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

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

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

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

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

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

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

@FunctionalInterface
public interface Consumer<T>

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

我们经常使用三元运算符来检查空条件并返回替代值或默认值。Optional 提供了另一种处理相同条件而不检查 null 的方法。如果 Optional 具有空值,则 Optional.orElse(defaultObj) 返回 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 免检查代码……真的吗?.

于 2015-04-27T04:16:49.873 回答
24

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

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

于 2011-08-21T18:33:43.130 回答
22

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

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

现在回答:

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

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

字节!

于 2008-11-13T22:08:04.167 回答
21

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一种方法,当需要检查某些内容是否为非空时,该方法可以派上用场。例子:

String lowerVal = Objects.requireNonNull(someVar, "input cannot be null or empty").toLowerCase();
于 2016-03-23T00:03:43.357 回答
17

我可以更笼统地回答它!

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

所以没有区别:

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 中实现此模式,您可以使用javax.annotation.NotNull 之类的核心 java 注释,或者使用Hibernate Validator等更复杂的库。

只是一个样本:

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

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

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

public class Car {

   @NotNull
   private String manufacturer;

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

   @Min(2)
   private int seatCount;

   // ...
}
于 2014-02-12T11:41:36.560 回答
17

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

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

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

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

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

于 2014-04-12T13:27:57.620 回答
16
  1. 永远不要将变量初始化为 null。
  2. 如果 (1) 不可行,请将所有集合和数组初始化为空集合/数组。

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

于 2013-06-14T13:05:31.063 回答
15

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

我们有很多方法来处理这个问题。

方法一:

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

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

方法二:

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;
}
于 2014-04-11T20:14:20.343 回答
12

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

Java 8 可选的优点:

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

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

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

于 2017-10-09T10:35:33.260 回答
12

总而言之,避免声明

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

    Objects.isNull(对象)

    Objects.nonNull(对象)

    Objects.requireNonNull(对象)

    Objects.equals(object1, object2)

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

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

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

  1. 依靠方法契约 ( JSR 305 ) 并使用Find Bugs。用注释@javax.annotation.Nullable@javax.annotation.Nonnnul. 也有先决条件。

    Preconditions.checkNotNull(object);

  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 时分配默认值

public static Object defaultIfNull(Object object, Object defaultValue)

于 2017-11-14T11:57:24.770 回答
11
public static <T> T ifNull(T toCheck, T ifNull) {
    if (toCheck == null) {
           return ifNull;
    }
    return toCheck;
}
于 2010-10-13T15:14:44.503 回答
8

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

于 2011-03-10T22:00:54.277 回答
8

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

  1. 尽量避免成员变量的惰性初始化。在声明本身中初始化变量。这将处理 NullPointerExceptions。

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

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

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

    例如,假设您的应用程序中的一个类 ( A.java) 正在维护一个集合,例如HashMap. 不要public在 A.java 中提供 getter 方法,允许B.java直接在 .java 中添加元素Map。而是提供一个 API A.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{}在正确的地方有效地使用积木。

于 2016-01-25T15:42:16.137 回答
8

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

  1. public static boolean isNull(Object obj)

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

    从 Java 1.8 开始

  2. public static boolean nonNull(Object obj)

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

    从 Java 1.8 开始

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

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

    从 Java 9 开始

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

    如果第一个参数非空,则返回第一个参数,否则返回供应商.get() 的非空值。

    从 Java 9 开始

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

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

    从 Java 1.8 开始

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

于 2018-07-12T17:35:17.980 回答
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");
于 2016-11-28T09:29:50.917 回答
7

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

例子:-

String myObject = null;

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

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

final String name = "Jobin";
String nonNullValue = Optional.ofNullable(name).filter(Objects::nonNull).orElse("DefaultName");
于 2017-07-04T09:41:26.430 回答
7

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

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

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

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

于 2019-06-11T12:39:56.207 回答
6

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

public class NonNullThing {

   Vector vectorField = new Vector();

   int[] arrayField = new int[0];

   public NonNullThing() {

      // etc

   }

}
于 2008-11-07T10:43:47.677 回答
6

另一种选择:

下面的简单函数有助于隐藏空检查(我不知道为什么,但我没有发现它是同一个公共库的一部分):

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

你现在可以写

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

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

于 2011-10-21T09:02:37.010 回答
6

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

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

例如,

Preconditions.checkNotNull(paramVal, "Method foo received null paramVal");
于 2014-03-05T22:44:01.493 回答
5

在 Java 8 中,您可以将 typeT用于 local-variable/field/method-argument/method-return-type 如果它从未分配null(并且不检查null)或 typeOptional<T>如果它可以是null。然后使用 method mapfor processingT ->和 method flatMapfor processing 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;}
}
于 2016-07-05T15:24:21.573 回答
4

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

假设 M1(Object test) 是一个方法,而 M2 是我们在方法调用之前应用方面的方法,M2(Object test2)...... 如果test2 != null然后调用M1,否则做另一件事。它适用于您要为其应用方面的所有方法。如果要为实例字段和构造函数应用方面,可以使用AspectJSpring也可以是方法方面的最佳选择。

于 2012-12-03T03:15:23.840 回答
4

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

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

或者

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

SomeClass 是某个对象的类型。

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

于 2019-05-15T09:14:00.267 回答
3

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

于 2012-03-23T20:31:43.973 回答
3

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

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

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

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

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

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

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

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

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

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

  2. 类中的方法还可以实例化本地对象以供内部使用,这些对象永远不会从类中传递出去,或者它们应该是普通的“拥有”对象。

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

在这种设计下,并遵循上述规则,层次设计中的子类永远不会访问被销毁的指针,因为这意味着父类在子类之前被销毁,而层次化非循环设计不会允许它。

最后,还请记住,在启动系统时,您应该从上到下构建层次结构并从下到上破坏。你永远不会在任何地方都有一个空指针,或者有人违反了规则。

于 2012-07-13T21:46:16.470 回答
3

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

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

于 2015-12-16T05:54:05.260 回答
3

使用 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();
        }
    }
于 2018-02-07T19:00:18.787 回答
3
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 和字符串的 isEmpty() 方法使用 null 检查来检查它。

简单来说,如果一个字符串不是 null 并且 isEmpty() 返回 false,那么它既不是 null 也不是空的。否则,它是。

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

于 2020-11-09T07:30:09.023 回答
2

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

基本思想是对象/参数/构造函数应始终满足先决条件。您可以有很多先决条件,例如Nullable,NotNull并且 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);
于 2013-01-10T07:45:59.320 回答
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 principleSSOLIDhttp://en.wikipedia.org/wiki/SOLID_(object-orientated_design)中,这意味着每段代码都应该有一个单一的责任,只做这个而不是别的)。一旦你的代码是干净的,就更容易推理它,也可以跨多个代码层/层。对于凌乱的代码,试图理解一个方法的作用可能会让你忘记你为什么要首先阅读这个方法。(提示:阅读 Robert C. Martin 的“清洁代码”)

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

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

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

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

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

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

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

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

public String nullSafeToString(final Object o) {
    return o != null ? o.toString() : "null";
}
于 2014-01-27T20:54:30.387 回答
2

有一种从 JDK 检查空值的好方法。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真的非常有用。

于 2020-10-21T02:23:33.447 回答
1

我更喜欢这个

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

当然,在我的示例中 SomeObject 可以优雅地处理空参数。例如记录此类事件并且什么都不做。

于 2012-09-12T14:54:01.960 回答
1

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

ObjectUtils.equals(object, null)

或者

CollectionUtils.isEmpty(myCollection);

或者

StringUtils.isEmpty("string");

作为一种实践,我喜欢之前的答案,即为集合提供初始默认值或空集以最大限度地减少需求。

这些可以是简单的用途,使您避免出现 NullPointerException 或使用空集合。这并没有回答如何处理 null 对象的问题,但这些提供了对对象或集合的基本验证的一些检查。

希望这可以帮助。

于 2013-10-31T19:18:24.977 回答
1

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

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

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

于 2015-11-17T17:37:37.150 回答
1

您拥有的一种选择

  • 在方法上使用检查器框架的 @RequiresNonNull 。例如,如果您调用一个带有这样注释的方法,并且带有一个空参数,那么您会得到这个。它会在编译期间失败,甚至在你的代码运行之前!因为在运行时它将是 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 的目标就没有关系

于 2017-12-27T04:35:17.313 回答
1

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

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

Object ob=null;
if(ob==null){ **do something}
于 2021-11-15T11:33:19.853 回答
0

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

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

于 2010-05-20T13:16:47.257 回答
0

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

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

于 2015-10-19T14:00:36.850 回答
0

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

于 2018-08-20T22:05:53.487 回答
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;} );
于 2019-01-03T06:21:26.727 回答
0

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

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
}
于 2020-08-09T10:12:28.283 回答
0

在 Java 中避免 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!");
};

但这取决于你。

于 2020-10-20T02:56:43.380 回答
-1

空对象模式可以用来解决这个问题。为此,应该修改 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

于 2016-10-04T08:57:46.360 回答
-5

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

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

于 2011-02-16T23:14:35.617 回答