我使用 x != null
来避免 NullPointerException
。有其他的替代方法吗?
if (x != null) {
// ...
}
我使用 x != null
来避免 NullPointerException
。有其他的替代方法吗?
if (x != null) {
// ...
}
Objects.requireNonNull(foo)
。(如果您被困在早期版本中,则 assert
ions 可能是一个不错的替代方案。)NullPointerException
。这意味着返回值始终是非空的。该方法主要用于验证参数。public Foo(Bar bar) {
this.bar = Objects.requireNonNull(bar);
}
如果对象为空,它也可以像断言一样使用,因为它会抛出异常。在这两种用法中,都可以添加消息,该消息将显示在异常中。以下是像断言一样使用并提供消息的示例。
Objects.requireNonNull(someobject, "if someobject is null then something is wrong");
someobject.doCalc();
NullPointerException
,比抛出更一般的异常,如AssertionError
,更可取。这是Java库采用的方法;当参数不允许为null时,优先选择NullPointerException
而不是IllegalArgumentException
。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()方法中抛出一个带有有意义的错误消息的异常可能是完全合适的,特别是在您依赖用户输入的情况下。与调用方法没有解释的简单NullPointerException相比,findAction方法抛出异常会更好。
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);
}
}
}
this.bar = Objects.requireNonNull(bar);
是使用这种方法的好处,因为你不会使用this.bar
。这样,当传递无效的空对象时,您会得到异常,而不是在尝试使用它时。但是,它经常被用于这种方式:
Objects.requireNonNull(getThing()).doThingStuff();
在这里,对Objects.requireNonNull()
的调用根本不改变行为,它无论如何都会抛出NPE。最好在保存对象以供以后使用时使用它。在我的第二个例子中,异常很有用。它有助于跟踪错误。 - MiguelMunoz@Nullable
和 @NotNull
两种选择。@NotNull public static String helloWorld() {
return "Hello World";
}
或者。@Nullable public static String helloWorld() {
return "Hello World";
}
第二个例子在 IntelliJ IDEA 中无法编译。
当您在另一段代码中使用第一个 helloWorld()
函数时:
public static void main(String[] args)
{
String result = helloWorld();
if(result != null) {
System.out.println(result);
}
}
现在,IntelliJ IDEA编译器会告诉您该检查是无用的,因为helloWorld()
函数不会返回null
。
使用参数
void someMethod(@NotNull someParameter) { }
如果你写了类似以下的代码:
someMethod(null);
这段代码无法编译。
最后一个例子使用了@Nullable
。
@Nullable iWantToDestroyEverything() { return null; }
这样做
iWantToDestroyEverything().something();
你可以确信这不会发生。 :)
这是一种很好的方式,让编译器检查比通常更多的内容,并加强您的契约。不幸的是,并非所有编译器都支持。
在 IntelliJ IDEA 10.5 及以上版本中,它们添加了对任何其他 @Nullable
@NotNull
实现的支持。
请参见博客文章 更灵活和可配置的 @Nullable/@NotNull 注释。
@NotNull
和@Nullable
接口存放在com.sun.istack.internal
包中很奇怪,这让我感到有点烦(我猜想是因为我会把com.sun与使用专有API的警告联系起来)。 - Jonik@NotNull
和 @Nullable
等注解在源代码被不理解它们的系统构建时可以很好地降级。因此,实际上,代码不可移植的论点可能是无效的 - 如果您使用支持并理解这些注解的系统,则可以获得更严格的错误检查的附加好处;否则,您将得到较少的错误检查,但您的代码仍应该能够正确构建,并且运行程序的质量是相同的,因为这些注解在运行时不被强制执行。此外,所有编译器都是定制的。;-) - Armen Michaeli如果您的方法被外部调用,请从以下类似的代码开始:
public void method(Object object) {
if (object == null) {
throw new IllegalArgumentException("...");
}
在那个方法的剩余部分,你会知道object
不是null。
如果这是一个内部方法(不是API的一部分),只需记录它不能为null即可。
例如:
public String getFirst3Chars(String text) {
return text.subString(0, 3);
}
然而,如果你的方法只是简单地将一个值传递给下一个方法等等,这可能会变得麻烦。在这种情况下,你可能需要像上面那样检查参数。
这取决于具体情况。我经常会做以下这样的事情:
if (object == null) {
// something
} else {
// something else
}
所以我会分支,做两件完全不同的事情。没有丑陋的代码片段,因为根据数据,我确实需要做两件不同的事情。例如,我应该处理输入还是计算一个良好的默认值?
我实际上很少使用习语“if (object != null && ...
”。
如果您展示通常使用此习语的示例,则可能更容易理解。
IllegalArgumentException
比普通的NPE
更好。我认为NPE
表示代码中某处不安全地取消引用了一个恰好为空的表达式(假设满足了所有已知和声明的前提条件和不变量)。另一方面,非法参数异常则告诉我未满足某个众所周知的前提条件或不变量。 - luis.espinal哇,我们已经有57种不同的方式推荐“NullObject模式”,我几乎不想再添加另一个答案了,但我认为一些对这个问题感兴趣的人可能想知道,在Java 7中有一个提案添加了"null-safe handling"-一种用于if-not-equal-null逻辑的简化语法。
Alex Miller给出的示例如下:
public String getPostcode(Person person) {
return person?.getAddress()?.getPostcode();
}
?.
的意思是,仅当左边的标识符不为null时才解引用它,否则将余下的表达式评估为null
。一些人,例如Java Posse成员Dick Wall和Devoxx的选民非常喜欢这个提案,但也有反对意见,认为它实际上会鼓励更多使用null
作为哨兵值。
更新:Java 7中关于空安全运算符的官方提案已经在Project Coin下提交。语法与上面的示例略有不同,但概念相同。
更新:空安全操作符提案未被纳入Project Coin。因此,在Java 7中您将看不到这种语法。
您可以配置您的IDE,以警告您可能存在的空指针引用。例如在Eclipse中,查看首选项> Java>编译器>错误/警告/空分析。
如果您想定义一个新的API,其中未定义的值有意义,请使用Option Pattern(可能来自函数式语言)。它具有以下优点:
Java 8内置了一个Optional
类(推荐使用);对于早期版本,有一些库可以替代,例如Guava的Optional
或FunctionalJava的Option
。但是像许多函数式模式一样,在Java中使用Option(即使是8)会导致相当多的样板代码,您可以使用一种不那么冗长的JVM语言,例如Scala或Xtend来减少这种情况。
如果您必须处理可能返回null的API,在Java中无法做太多事情。Xtend和Groovy具有Elvis运算符 ?:
和空安全引用运算符 ?.
,但请注意,如果引用为null,则返回null,因此它仅“延迟”了对null的正确处理。
仅适用于此情况 -
在调用equals方法之前不检查变量是否为空(下面是一个字符串比较的例子):
if ( foo.equals("bar") ) {
// ...
}
如果foo
不存在,则会导致NullPointerException
。
您可以通过以下方式比较String
来避免这种情况:
if ( "bar".equals(foo) ) {
// ...
}
<已知非空对象>.equals(<可能为空的对象>)
。 如果你知道契约并且这些方法可以处理 null
参数,则对于其他方法也适用,不仅限于 equals
。 - Steffoo
,该怎么办?
如果一个变量不应该为 null,但实际上却是...你的应用程序需要得到 NPE,这样作为开发者的你才能真正修复潜在的原因。 - Arnab Datta随着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
中搜索:
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/空值仍然是必要的,但至少开发人员意识到该值可能为空,并且忘记检查的风险有限。
在使用Optional
构建API时,每当返回值可能为空时都使用Optional
,并且仅在无法为空时返回普通对象(约定),客户端代码可能会放弃对简单对象返回值进行null检查...
当然,Optional
也可以用作方法参数,这也许是在某些情况下比使用5或10个重载方法来指示可选参数更好的方式。
Optional
提供了其他方便的方法,例如orElse
允许使用默认值,以及与lambda表达式一起工作的ifPresent
。
我邀请您阅读这篇文章(我写这篇答案的主要来源),其中详细解释了NullPointerException
(以及一般的空指针)问题,以及Optional
带来的(部分)解决方案:Java Optional Objects。
if(optional.isPresent()){ optional.get(); }
而不是 optional.ifPresent(o -> { ...})
? - Satyendra Kumar根据您要检查的对象类型,您可以使用apache commons中的一些类,例如:apache commons lang和apache commons collections
示例:
String foo;
...
if( StringUtils.isBlank( foo ) ) {
///do something
}
String foo;
...
if( StringUtils.isEmpty( foo ) ) {
///do something
}
StringUtils类只是众多类中的一个; commons中有许多优秀的类可以进行null安全操作。
以下是一个示例,展示了您在包含apache库(commons-lang-2.4.jar)时如何在JAVA中使用null验证。
public DOCUMENT read(String xml, ValidationEventHandler validationEventHandler) {
Validate.notNull(validationEventHandler,"ValidationHandler not Injected");
return read(new StringReader(xml), true, validationEventHandler);
}
如果你正在使用Spring框架,它的包中也有同样的功能库,请查看library(spring-2.4.6.jar)
示例演示如何使用Spring的静态类(org.springframework.util.Assert)
Assert.notNull(validationEventHandler,"ValidationHandler not Injected");
只有当你想处理对象可能为null的情况时,才需要检查对象!=null...
Java7中有一个提案,用于帮助处理null/notnull参数:http://tech.puredanger.com/java7/#jsr308
我是“快速失败”的代码粉丝。问问自己 - 如果参数为空,您是否在做有用的事情?如果您对代码在这种情况下应该执行什么没有明确的答案...即-它在第一次出现时就不应该为空,则忽略它并允许抛出NullPointerException
。调用代码将对抛出NPE和IllegalArgumentException
有同样的理解,但如果抛出NPE而不是您的代码尝试执行一些其他意外的容错逻辑,开发人员将更容易地调试和理解出了什么问题- 最终导致应用程序失败。