我用 object != null
要避免的很多 NullPointerException
。
有没有一个很好的替代品呢?
例如:
if (someobject != null) {
someobject.doCalc();
}
这避免了 NullPointerException
,当不知道对象是否是 null
或不。
请注意,接受的答案可能已过期,请参阅 https://stackoverflow.com/a/2386013/12943 对于更近期的方法。
我用 object != null
要避免的很多 NullPointerException
。
有没有一个很好的替代品呢?
例如:
if (someobject != null) {
someobject.doCalc();
}
这避免了 NullPointerException
,当不知道对象是否是 null
或不。
请注意,接受的答案可能已过期,请参阅 https://stackoverflow.com/a/2386013/12943 对于更近期的方法。
这对我来说听起来像是一个相当普遍的问题,初级到中级开发人员往往会在某些方面面临这样的问题:他们要么不知道,要么不信任他们参与的合同,并且防御性地过度检查空值。另外,在编写自己的代码时,它们倾向于依赖返回空值来指示某些东西,从而要求调用者检查空值。
换句话说,有两个实例进行空检查:
如果null是合同方面的有效回复;和
哪里不是有效的回复。
(2)很容易。要么使用 assert
陈述(断言)或允许失败(例如, 空指针异常)。断言是1.4中添加的高度未充分利用的Java功能。语法是:
assert <condition>
要么
assert <condition> : <object>
哪里 <condition>
是一个布尔表达式 <object>
是一个对象 toString()
方法的输出将包含在错误中。
一个 assert
声明抛出一个 Error
(AssertionError
)如果条件不正确。默认情况下,Java会忽略断言。您可以通过传递选项来启用断言 -ea
到JVM。您可以为各个类和包启用和禁用断言。这意味着您可以在开发和测试时使用断言验证代码,并在生产环境中禁用它们,尽管我的测试表明,断言没有性能影响。
在这种情况下不使用断言是可以的,因为代码只会失败,如果使用断言将会发生这种情况。唯一的区别是,断言可能会更快地发生,以更有意义的方式发生,并且可能还有额外的信息,这可能会帮助您弄清楚如果您不期望它发生的原因。
(1)有点难。如果您无法控制您正在调用的代码,那么您就会陷入困境。如果null是有效响应,则必须检查它。
如果它是你控制的代码(然而通常就是这种情况),那么这是一个不同的故事。避免使用空值作为响应。使用返回集合的方法,很容易:几乎一直返回空集合(或数组)而不是null。
对于非收藏,它可能更难。以此为例:如果您有这些接口:
public interface Action {
void doSomething();
}
public interface Parser {
Action findAction(String userInput);
}
Parser采用原始用户输入并找到要做的事情,也许是在为某些事情实现命令行界面时。现在,如果没有适当的操作,您可以使合同返回null。这导致你正在谈论的空检查。
另一种解决方案是永远不会返回null而是使用 空对象模式:
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);
}
}
}
如果您使用(或计划使用)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
@NotNull
实现。
如果您的方法是外部调用的,请从以下内容开始:
public void method(Object object) {
if (object == null) {
throw new IllegalArgumentException("...");
}
然后,在该方法的其余部分,你会知道 object
不是空的。
如果它是一个内部方法(不是API的一部分),只需记录它不能为null,就是这样。
例:
public String getFirst3Chars(String text) {
return text.subString(0, 3);
}
但是,如果您的方法只是传递了值,并且下一个方法将其传递给它,则可能会出现问题。在这种情况下,您可能需要检查上面的参数。
这真的取决于。如果发现我经常做这样的事情:
if (object == null) {
// something
} else {
// something else
}
所以我分支,做两件完全不同的事情。没有丑陋的代码片段,因为我真的需要根据数据做两件事。例如,我应该处理输入,还是应该计算一个好的默认值?
我真的很少使用这个成语“if (object != null && ...
”。
如果您显示通常使用成语的示例,则可能更容易为您提供示例。
哇,当我们有57种不同的推荐方法时,我几乎不想添加另一个答案 NullObject pattern
,但我认为有些人对这个问题感兴趣,可能想知道Java 7上有一个提议要添加 “无效安全处理” - 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安全运算符已经提交 项目硬币。 语法与上面的示例略有不同,但它是相同的概念。
更新: 无效的运营商提案没有进入项目硬币。因此,您将不会在Java 7中看到此语法。
您可以配置IDE以警告您可能存在空取消引用。例如。在Eclipse中,请参阅 首选项> Java>编译器>错误/警告/空分析。
如果要定义一个新的API,其中未定义的值是有意义的, 使用 选项模式 (可能熟悉函数式语言)。它具有以下优点:
Java 8有一个内置的 Optional
上课(推荐);例如,对于早期版本,有库替代品 番石榴的 Optional
要么 FunctionalJava的 Option
。但是,与许多功能样式模式一样,在Java中使用Option(甚至8)会产生相当多的样板,使用较简洁的JVM语言可以减少这种样板,例如: Scala或Xtend。
如果必须处理可能返回null的API,你在Java中做不了多少。 Xtend和Groovy有 猫王经营者 ?:
和 零安全解除引用运算符 ?.
,但请注意,如果是空引用,则返回null,因此它只是“延迟”对null的正确处理。
仅限于这种情况 -
在调用equals方法之前不检查变量是否为null(下面的字符串比较示例):
if ( foo.equals("bar") ) {
// ...
}
会导致一个 NullPointerException
如果 foo
不存在。
如果你比较你的,你可以避免这种情况 String
是这样的:
if ( "bar".equals(foo) ) {
// ...
}
随着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");
当然,检查null / empty值仍然是必要的,但至少开发人员意识到该值可能是空的并且忘记检查的风险是有限的。
在从头开始构建的API中使用 Optional
每当返回值可能为空时,只有当它不能返回时才返回普通对象 null
(约定),客户端代码可能会放弃对简单对象返回值的空检查...
当然 Optional
也可以用作方法参数,在某些情况下,可能是指示可选参数而不是5或10重载方法的更好方法。
Optional
提供其他方便的方法,如 orElse
允许使用默认值,和 ifPresent
适用于 lambda表达式。
我邀请你阅读这篇文章(我写这个答案的主要来源) NullPointerException
(以及一般的空指针)问题以及由此带来的(部分)解决方案 Optional
很好解释: Java可选对象。
根据您检查的对象类型,您可以使用apache公共中的某些类,例如: apache commons lang 和 apache commons集合
例:
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 ...
有人建议在Java7中添加新的注释来帮助null / notnull参数: http://tech.puredanger.com/java7/#jsr308
我是“快速失败”代码的粉丝。问问自己 - 在参数为null的情况下,你是否正在做一些有用的事情?如果你没有明确的答案,你的代码在这种情况下应该做些什么...... I.e。它首先应该永远不为null,然后忽略它并允许抛出NullPointerException。调用代码与IllegalArgumentException一样具有NPE意义,但是如果抛出NPE而不是代码试图执行其他意外的意外事件,开发人员将更容易调试并理解出现了什么问题。逻辑 - 最终导致应用程序失败。
有时,您有一些方法可以对其参数进行操作,从而定义对称操作:
a.f(b); <-> b.f(a);
如果你知道b永远不能为空,你可以交换它。它对equals最有用:
代替 foo.equals("bar");
更好 "bar".equals(foo);
。