提问人: 提问时间:11/7/2008 最后编辑:38 revs, 30 users 11%Goran Martinic 更新时间:11/8/2023 访问量:1427699
如何避免在 Java 中检查空值?
How do I avoid checking for nulls in Java?
答:
与其说是 Null 对象模式(有其用途),不如考虑 null 对象是 bug 的情况。
引发异常时,请检查堆栈跟踪并解决 bug。
评论
- 如果您认为某个对象不应为 null(或者它是一个 bug),请使用断言。
- 如果你的方法不接受 null 参数,请在 javadoc 中说出来并使用断言。
仅当您要处理对象可能为 null 的情况时,才必须检查对象 != null...
有人建议在 Java7 中添加新的注解来帮助处理 null / notnull 参数: http://tech.puredanger.com/java7/#jsr308
评论
有时,您可以使用一些方法对其参数进行操作,这些参数定义了对称操作:
a.f(b); <-> b.f(a);
如果你知道 b 永远不能为空,你可以交换它。它对等于最有用:
而不是最好做.foo.equals("bar");
"bar".equals(foo);
评论
equals
equals
null
根据您正在检查的对象类型,您可以使用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 中使用空值
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");
评论
如果不允许 null 值
如果方法是在外部调用的,请从如下所示的内容开始:
public void method(Object object) {
if (object == null) {
throw new IllegalArgumentException("...");
}
然后,在该方法的其余部分,您将知道它不是 null。object
如果它是一个内部方法(不是 API 的一部分),只需记录它不能为 null,仅此而已。
例:
public String getFirst3Chars(String text) {
return text.subString(0, 3);
}
但是,如果你的方法只是传递值,而下一个方法传递它,等等,它可能会有问题。在这种情况下,您可能需要检查上述参数。
如果允许 null
这真的要看情况。如果发现我经常做这样的事情:
if (object == null) {
// something
} else {
// something else
}
所以我分支,做两件完全不同的事情。没有丑陋的代码片段,因为我真的需要根据数据做两件不同的事情。例如,我应该处理输入,还是应该计算一个好的默认值?
实际上,我很少使用成语“”。if (object != null && ...
如果您举例说明您通常在哪里使用该成语,那么举例可能会更容易。
评论
throw new IllegalArgumentException("object==null")
IllegalArgumentException
NPE
提出这个问题表明您可能对错误处理策略感兴趣。如何以及在何处处理错误是一个普遍存在的架构问题。有几种方法可以做到这一点。
我最喜欢的:允许异常涟漪 - 在“主循环”或其他具有适当职责的函数中捕获它们。检查错误情况并妥善处理它们可以被视为一项专门的责任。
当然,也要看看面向方面的编程 - 他们有巧妙的方法可以插入到你的字节码中。if( o == null ) handleNull()
无论您在何处传递数组或 Vector,请将它们初始化为空,而不是 null。- 这样你就可以避免大量的空值检查,一切都很好:)
public class NonNullThing {
Vector vectorField = new Vector();
int[] arrayField = new int[0];
public NonNullThing() {
// etc
}
}
在我看来,这听起来像是一个相当普遍的问题,初级到中级开发人员在某个时候往往会面临:他们要么不知道,要么不信任他们参与的合同,并且防御性地过度检查空值。此外,在编写自己的代码时,它们倾向于依赖返回 null 来指示某些内容,因此需要调用方检查 null。
换句话说,有两种情况下会出现 null 检查:
其中 null 是合同方面的有效响应;和
它不是有效的响应。
(2)容易。从 Java 1.7 开始,您可以使用 Objects.requireNonNull(foo)。
(如果您坚持使用以前的版本,那么断言
离子可能是一个不错的选择。
此方法的“正确”用法如下所示。该方法返回传递给它的对象,如果对象为 null,则抛出 。这意味着返回的值始终为非 null。该方法主要用于验证参数。NullPointerException
public Foo(Bar bar) {
this.bar = Objects.requireNonNull(bar);
}
它也可以像离子一样使用,因为如果对象为 null,它会引发异常。在这两种用途中,都可以添加一条消息,该消息将显示在异常中。下面是像断言一样使用它并提供消息。assert
Objects.requireNonNull(someobject, "if someobject is null then something is wrong");
someobject.doCalc();
通常抛出特定的异常(例如,当值为 null 但不应为 null 时)有利于抛出更一般的异常,例如 。这是 Java 库采用的方法;当不允许参数为 null 时,优先。NullPointerException
AssertionError
NullPointerException
IllegalArgumentException
(1)有点难。如果你无法控制你正在调用的代码,那么你就被卡住了。如果 null 是有效的响应,则必须检查它。
但是,如果代码由您控制(通常情况如此),那么情况就不同了。避免使用 null 值作为响应。使用返回集合的方法,这很容易:几乎一直返回空集合(或数组)而不是 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() 方法抛出带有有意义的错误消息的 Exception 是完全合适的——尤其是在您依赖用户输入的情况下。findAction 方法抛出 Exception 比调用方法用简单的 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);
}
}
}
评论
this.bar = Objects.requireNonNull(bar);
this.bar
Objects.requireNonNull(getThing()).doThingStuff();
Objects.requireNonNull()
仅适用于这种情况——
在调用 equals 方法之前不检查变量是否为 null(下面的字符串比较示例):
if ( foo.equals("bar") ) {
// ...
}
将导致 if 不存在。NullPointerException
foo
如果你像这样比较你的 s,你可以避免这种情况:String
if ( "bar".equals(foo) ) {
// ...
}
评论
<object that you know that is not null>.equals(<object that might be null>);
equals
null
foo
我已经尝试过了,但对我来说并不总是最好的方法。有时,“不采取行动”是不合适的。NullObjectPattern
NullPointerException
是一个运行时异常,这意味着它是开发人员的错,并且有足够的经验它会告诉你错误在哪里。
现在来回答:
尝试使所有属性及其访问器尽可能私有,或者完全避免将它们暴露给客户端。当然,您可以在构造函数中包含参数值,但是通过缩小范围,您不会让客户端类传递无效值。如果需要修改这些值,可以随时创建一个新的 .只需检查构造函数中的值一次,在其余方法中,几乎可以确定这些值不为 null。object
当然,经验是理解和应用这一建议的更好方法。
字节!
Google 集合框架提供了一种实现 null 检查的良好而优雅的方法。
库类中有一个方法,如下所示:
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();
评论
getThing().getItsThing().getOtherThing().wowEncapsulationIsBroken().setLol("hi");
哇,当我们有 57 种不同的方法来推荐 时,我几乎不想添加另一个答案,但我认为一些对这个问题感兴趣的人可能想知道 Java 7 有一个建议,即添加“null-safe handling”——if-not-equal-null 逻辑的简化语法。NullObject pattern
Alex Miller 给出的示例如下所示:
public String getPostcode(Person person) {
return person?.getAddress()?.getPostcode();
}
如果左标识符不为 null,则仅取消引用左标识符,否则将表达式的其余部分计算为 。有些人,比如 Java Posse 成员 Dick Wall 和 Devoxx 的选民非常喜欢这个提议,但也有反对意见,理由是它实际上会鼓励更多地使用作为哨兵值。?.
null
null
更新:Java 7 中 null 安全运算符的官方提案已在 Project Coin 下提交。语法与上面的示例略有不同,但概念相同。
更新:空安全算子提案没有进入 Project Coin。因此,您不会在 Java 7 中看到这种语法。
评论
最终,完全解决这个问题的唯一方法是使用不同的编程语言:
- 在 Objective-C 中,您可以执行等效于 调用方法 的操作,并且绝对不会发生任何事情。这使得大多数 null 检查变得不必要,但它会使错误更难诊断。
nil
- 在 Nice(一种 Java 派生语言)中,所有类型都有两个版本:潜在 null 版本和非 null 版本。只能对非 null 类型调用方法。通过显式检查 null,可以将潜在的 null 类型转换为非 null 类型。这样可以更容易地知道哪些地方需要 null 检查,哪些地方不需要。
评论
如果不允许使用未定义的值:
您可以将 IDE 配置为警告您潜在的 null 取消引用。例如,在 Eclipse 中,请参阅 Java > Compiler > Errors/Warnings/Null 分析>首选项。
如果允许未定义的值:
如果要定义一个新 API,其中未定义的值是有意义的,请使用 Option Pattern(可能熟悉函数式语言)。它具有以下优点:
- 在 API 中明确说明是否存在输入或输出。
- 编译器会强制您处理“未定义”的情况。
- Option 是一个 monad,因此不需要进行冗长的 null 检查,只需使用 map/foreach/getOrElse 或类似的组合器来安全地使用该值(示例)。
Java 8 有一个内置的 Optional
类(推荐);对于早期版本,有一些库替代,例如 Guava 的 Optional
或 FunctionalJava 的 Option
。但就像许多函数式模式一样,在 Java(甚至 8)中使用 Option 会产生相当多的样板,您可以使用不太冗长的 JVM 语言(例如 Scala 或 Xtend)来减少样板。
如果你必须处理一个可能返回 null 的 API,那么你在 Java 中就无能为力了。Xtend 和 Groovy 具有 Elvis 运算符和 null 安全取消引用运算符,但请注意,在 null 引用的情况下,这将返回 null,因此它只是“推迟”对 null 的正确处理。?:
?.
评论
如果您使用(或计划使用)像 JetBrains IntelliJ IDEA、Eclipse 或 Netbeans 这样的 Java IDE,或者像 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
请参阅博客文章更灵活和可配置的 @Nullable/@NotNull 注释。
评论
@NotNull
@Nullable
com.sun.istack.internal
@NotNull
@Nullable
对于实用程序类,可以检查参数是否为空。
在所有其他情况下,您可能不必这样做。尽可能多地使用封装,从而减少您想要检查 null 的地方。
除了使用之外,您还可以使用以下功能:assert
if (someobject == null) {
// Handle null here then move on.
}
这比以下情况略好:
if (someobject != null) {
.....
.....
.....
}
评论
if (!something) { x(); } else { y(); }
if (something) { y(); } else { x(); }
!= null
{}
public static <T> T ifNull(T toCheck, T ifNull) {
if (toCheck == null) {
return ifNull;
}
return toCheck;
}
评论
ObjectUtils.defaultIfNull()
ObjectUtils.firstNonNull()
firstNonNull(bestChoice, secondBest, thirdBest, fallBack);
另一个建议是防御性编程 - 其中类/函数提供已知且安全的默认值,并且 null 保留用于真正的错误/异常。
例如,当出现问题时,不要返回返回 null 的 String(例如将数字转换为字符串),而是让它们返回空字符串 (“”)。在继续操作之前,您仍然需要测试返回值,但不会有异常的特殊情况。这种编程方式的另一个好处是,程序将能够区分正常操作和异常并做出相应的响应。
评论
您可以使用 FindBugs。他们还有一个 Eclipse 插件),可以帮助您找到重复的 null 检查(除其他外),但请记住,有时您应该选择防御性编程。还有 Java 合同可能会有所帮助。
确实是 Java 中的常见“问题”。
首先,我对此的看法:
我认为在NULL 不是有效值的情况下传递 NULL 时“吃”一些东西是不好的。如果您没有以某种错误退出方法,则意味着您的方法没有出错,这是不正确的。那么在这种情况下,你可能会返回 null,在接收方法中,你再次检查 null,它永远不会结束,你最终会得到“if != null”,等等。
因此,恕我直言,null 必须是一个严重错误,它会阻止进一步执行(即,null 不是有效值)。
我解决这个问题的方法是这样的:
首先,我遵循以下约定:
- 所有公共方法/API 始终检查其参数是否为 null
- 所有私有方法都不会检查 null,因为它们是受控方法(如果上面没有处理,就让 nullpointer 异常死亡)
- 唯一不检查 null 的其他方法是实用程序方法。它们是公共的,但如果您出于某种原因调用它们,您就会知道您传递了哪些参数。这就像试图在不提供水的情况下将水壶中的水烧开......
最后,在代码中,公共方法的第一行是这样的:
ValidationUtils.getNullValidator().addParam(plans, "plans").addParam(persons, "persons").validate();
请注意,addParam() 返回 self,以便您可以添加更多参数进行检查。
如果任何参数为 null,方法将抛出 checked(checked 或 unchecked 更像是设计/品味问题,但 my 被检查)。validate()
ValidationException
ValidationException
void validate() throws ValidationException;
例如,如果“plans”为 null,则消息将包含以下文本:
"参数 [plans] 遇到非法参数值 null"
正如你所看到的,addParam() 方法(字符串)中的第二个值是用户消息所必需的,因为即使有反射,你也无法轻松检测传入的变量名(反正不是这篇文章的主题......
是的,我们知道,超过这一行,我们将不再遇到 null 值,因此我们只是安全地在这些对象上调用方法。
这样,代码就干净、易于维护和可读。
评论
我是“快速失败”代码的粉丝。问问自己 - 在参数为 null 的情况下,您是否在做一些有用的事情?如果你对你的代码在这种情况下应该做什么没有一个明确的答案......即 - 它首先不应该是 null,然后忽略它并允许抛出 a。调用代码对 NPE 的意义与对 的意义一样大,但是如果抛出 NPE,开发人员会更容易调试和理解出了什么问题,而不是您的代码试图执行其他一些意外的应急逻辑 - 这最终会导致应用程序失败。NullPointerException
IllegalArgumentException
评论
我喜欢 Nat Pryce 的文章。以下是链接:
在文章中,还有一个指向 Java 可能类型的 Git 存储库的链接,我觉得这很有趣,但我认为仅凭它并不能减少 检查代码膨胀。在互联网上做了一些研究后,我认为 != null 代码膨胀主要可以通过精心设计来减少。
评论
另一种选择:
以下简单函数有助于隐藏空检查(我不知道为什么,但我还没有找到它作为同一公共库的一部分):
public static <T> boolean isNull(T argument) {
return (argument == null);
}
你现在可以写
if (!isNull(someobject)) {
someobject.doCalc();
}
这是IMO更好的表达方式。!= null
评论
isNotNull
isNull
isNotNull
Null 不是“问题”。它是完整建模工具集的组成部分。软件旨在模拟世界的复杂性,而 null 承担了它的负担。Null 表示 Java 中的“无数据”或“未知”。因此,将 null 用于这些目的是合适的。我不喜欢“Null 对象”模式;我认为它上升了“谁来守卫”
守护者的问题。
如果你问我女朋友叫什么名字,我会告诉你我没有女朋友。在 Java 语言中,我将返回 null。
另一种方法是抛出有意义的异常,以指示某些无法(或不想)解决的问题,并将其委托给堆栈中更高位置,以重试或向用户报告数据访问错误。
对于“未知问题”,请给出“未知答案”。(从业务角度来看,如果这是正确的,则为 null 安全)在使用方法之前在方法中检查一次 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);
不要讨厌输入(在 Eclipse 中生成 javadoc)并为你的公共 API 编写三个额外的单词。对于除了那些不阅读文档的人来说,这将绰绰有余。
<alt> + <shift> + <j>
/** * @return photo or null */
或
/** * @return photo, never null */
这是相当理论上的情况,在大多数情况下,您应该更喜欢 java null 安全 API(以防它将在 10 年后发布),但
NullPointerException
是Exception
的子类。因此,它是一种形式,指示合理的应用程序可能想要捕获的条件 (javadoc)!为了利用异常的第一个最大优势,并将错误处理代码与“常规”代码分开(根据 Java 的创建者的说法),对我来说,捕获 .Throwable
NullPointerException
public Photo getGirlfriendPhoto() { try { return appContext.getPhotoDataSource().getPhotoByName(me.getGirlfriend().getName()); } catch (NullPointerException e) { return null; } }
可能会出现以下问题:
Q.如果返回 null 怎么办?
一个。这取决于业务逻辑。如果我找不到相册,我不会给你看照片。如果 appContext 未初始化怎么办?此方法的业务逻辑可以忍受这一点。如果相同的逻辑应该更严格,那么抛出异常,它是业务逻辑的一部分,应该使用对 null 的显式检查(案例 3)。新的 Java Null 安全 API 更适合于此,可以有选择地指定在程序员出错时初始化为快速失败的隐含内容和不隐含的内容。getPhotoDataSource()
问:可以执行冗余代码,并可以获取不必要的资源。
一个。如果尝试打开数据库连接,最后创建并使用人名作为 SQL 参数,则可能会发生这种情况。未知问题给出未知答案的方法(案例 1)在这里有效。在获取资源之前,该方法应检查参数并在需要时返回“未知”结果。getPhotoByName()
PreparedStatement
Q.由于尝试关闭打开,此方法会降低性能。
答:软件首先要易于理解和修改。只有在这之后,人们才能考虑性能,而且只有在需要时!以及需要的地方!(来源)和许多其他人)。这种方法的使用方式与将错误处理代码与“常规”代码原则分开使用一样合理。请看下面的例子:
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 是 的子类。我们在 Java 历史上有一个先例,不是因为它实际上是一个应用程序错误,而仅仅是因为它不打算被捕获!NPE适合多少!但事实并非如此。
Exception
ThreadDeath
Error
Error
ThreadDeath
仅当业务逻辑暗示时,才检查“无数据”。
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 问题,这实际上是应用程序错误。程序员应该在初始化内容中的某个地方修复错误。
评论
NullPointerException
null
if (maybeNull.hasValue()) {...}
if (maybeNull != null)) {...}
Maybe<T>
Optional<T>
T
T
Guava 是 Google 的一个非常有用的核心库,它有一个很好且有用的 API 来避免空值。我发现UsingAndAvoidingNullExplained非常有帮助。
正如 wiki 中所解释的:
Optional<T>
是一种将可为 null 的 T 引用替换为 非 null 值。Optional 可以包含非空 T 引用 (在这种情况下,我们说引用是“存在的”),或者它可能包含 什么都没有(在这种情况下,我们说引用是“不存在的”)。它从来都不是 说“包含 null”。
用法:
Optional<Integer> possible = Optional.of(5);
possible.isPresent(); // returns true
possible.get(); // returns 5
评论
您还可以使用 Checker Framework(使用 JDK 7 及更高版本)静态检查 null 值。这可能会解决很多问题,但需要运行一个目前仅适用于 OpenJDK AFAIK 的额外工具。https://checkerframework.org/
好吧,我现在在技术上已经回答了一百万次,但我不得不这么说,因为这是与 Java 程序员的无休止的讨论。
对不起,但我不同意几乎所有以上。我们必须在 Java 中测试 null 的原因是 Java 程序员不知道如何处理内存。
我这样说是因为我有很长的 C++ 编程经验,但我们不这样做。换句话说,你不需要。请注意,在 Java 中,如果你点击了一个悬空的指针,你会得到一个正常的异常;在 C++ 中,此异常通常不会被捕获并终止程序。
不想这样做?然后遵循一些简单的规则,ala C/C++。
不要那么容易地实例化事物,认为每个“新”都会给你带来很多麻烦,并遵循这些简单的规则。
一个类只能以 3 种方式访问内存 ->
它可以“拥有”类成员,他们将遵循以下规则:
- 所有“HAS”成员都是在构造函数中创建“new”的。
- 您将在析构函数或等效的 close() 中关闭 /de 分配 Java 中的函数,而不是其他类。
这意味着您需要牢记(就像 Java 一样)谁是每个资源的所有者或父级,并尊重该所有权。对象仅由创建对象的类删除。还有 ->
有些成员将被“使用”,但不是拥有或“拥有”。这是另一个类中的“OWN”,并作为参数传递给构造函数。由于这些归另一个类所有,我们永远不会删除或关闭它,只有父类可以。
类中的方法还可以实例化本地对象供内部使用,这些对象永远不会传递到类的一侧,或者它们应该是正常的“has”对象。
最后,要使所有这些工作都起作用,您需要有一个规范的设计,以层次结构形式进行类,并且不进行循环。
在这种设计下,并且遵循上述规则,层次结构设计中的子类不可能访问被销毁的指针,因为这意味着父类在子类之前被销毁,而分层非循环设计不允许这样做。
最后,还要记住,在启动系统时,您应该从层次结构的顶部到底部构建并销毁自下而上的销毁。您永远不会在任何地方出现空指针,或者有人违反规则。
评论
Java 7 有一个新的实用程序类,上面有一个方法。如果其参数为 null,则这样做只是抛出一个,但它会稍微清理代码。例: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");
}
评论
doCalc(someObject)
doCalc()
Objects.requireNonNull()
我更喜欢这个
public void simpleFunc(SomeObject someObject){
someObject = someObject != null ? someObject : new SomeObject(null);
someObject.doSomething();
}
当然,在我的示例中,SomeObject 优雅地处理一个 null 参数。例如,记录此类事件,然后不执行其他操作。
只是永远不要使用 null。不允许。
在我的类中,大多数字段和局部变量都具有非 null 默认值,我在代码中的任何地方添加 contract 语句(always-on 断言)以确保强制执行(因为它更简洁,比让它作为 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,你必须以某种方式弄清楚。
评论
可以在方法调用之前使用侦听器。这就是面向方面的编程所关注的。
假设 M1(对象测试)是一个方法,而 M2 是一个方法,我们在方法调用之前应用了一个方面。如果然后调用 M1,否则执行其他操作。它适用于您要为其应用某个方面的所有方法。如果要为实例字段和构造函数应用方面,可以使用 AspectJ。在方法方面,Spring 也可以是最佳选择。M2(Object test2)
test2 != null
评论
首先,我们不能真正删除所有 null 条件。我们可以使用和注释(如前所述)来减少它们。但这需要一些框架的支持。这就是 OVal 可以提供帮助的地方。@NotNull
@Nullable
基本思想是对象/参数/构造函数应始终满足前提条件。你可以有很多前提条件,比如 ,OVal 会注意对象在调用时应该处于一致的状态。Nullable
NotNull
我猜 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);
Java 8 带来了一个可以说解决了一些问题的新类。至少可以说它提高了代码的可读性,并且在公共 API 的情况下,使 API 的契约对客户端开发人员来说更清晰。java.util.Optional
他们的工作方式是这样的:
给定类型 () 的可选对象被创建为方法的返回类型。它可以是空的,也可以包含一个对象: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 实例中搜索 () 列表: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/empty 值的检查仍然是必要的,但至少开发人员意识到该值可能是空的,忘记检查的风险是有限的。
在从头开始构建的 API 中,每当返回值可能为空时,并且仅在不能返回时才返回纯对象(约定),客户端代码可能会放弃对简单对象返回值的 null 检查......Optional
null
当然,也可以用作方法参数,在某些情况下,也许比 5 或 10 个重载方法更适合指示可选参数。Optional
Optional
提供了其他方便的方法,例如允许使用默认值的方法,以及适用于 lambda 表达式的方法。orElse
ifPresent
我邀请您阅读这篇文章(我写这个答案的主要来源),其中很好地解释了(通常是空指针)问题以及(部分)解决方案:Java 可选对象。NullPointerException
Optional
评论
if(optional.isPresent()){ optional.get(); }
optional.ifPresent(o -> { ...})
- 切勿将变量初始化为 null。
- 如果 (1) 不可行,则将所有集合和数组初始化为空集合/数组。
在您自己的代码中执行此操作,您可以避免 != null 检查。
大多数时候,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 来说,这是值得的。
评论
我们一直在使用 Apache 库 (Apache Commons) 来解决这个问题。
ObjectUtils.equals(object, null)
或
CollectionUtils.isEmpty(myCollection);
或
StringUtils.isEmpty("string");
我喜欢前面的答案,作为一种做法,为集合提供初始默认值或空集,以最大程度地减少需求。
这些可能是简单的用法,可防止您具有 NullPointerException 或使用空集合。这并不能回答如何处理 null 对象的问题,但这些对象为对象或集合的基本验证提供了一些检查。
希望这会有所帮助。
评论
CollectionUtils.isEmpty
StringUtils.isEmpty
避免不必要的方法很简单: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
这个问题没有快速解决的简单答案,但这里有一些提示:
干净的代码。能够推理一段代码的行为,最重要的是它是用易于理解的问题编写的。根据变量所代表的内容命名变量,以它们的作用命名方法,应用 (in : http://en.wikipedia.org/wiki/SOLID_(object-oriented_design),这意味着每段代码都应该有一个职责,并且只做这个,不做别的)。一旦你的代码是干净的,就更容易推理它,也可以跨多个代码层/层。对于杂乱无章的代码,试图理解方法的作用可能会让你忘记你为什么要阅读这个方法。(提示:阅读 Robert C. Martin 的“Clean Code”)
Single responsibility principle
S
SOLID
避免返回值。如果某个值会阻止程序正常运行,请抛出一个值(确保添加适当的错误处理)。例如,返回值可能是可以接受的,例如尝试从数据库中获取对象。在这些情况下,请编写处理这些值的代码,并在耳后记下,这里有一些可能会返回的内容。处理返回的值尽可能接近返回方法的调用方(不要盲目地将其传递回调用链。
null
null
exception
null
null
null
null
null
永远不要将显式值作为参数传递(至少不要跨类传递)。如果您曾经处于传递 -parameter 是唯一选项的位置,那么创建一个没有此参数的新方法是要走的路。
null
null
验证您的输入!确定应用程序的“入口点”。它们可以包括 Web 服务、REST 服务、远程 EJB 类、控制器等。对于这些入口点中的每个方法,问问自己:“如果此参数为 null,此方法是否正确执行?如果答案是否定的,请添加 。如果缺少所需的参数,这将抛出一个。在入口点中进行这种类型的验证的好处是,您可以轻松地在从入口点执行的代码中假设此变量永远不会为空!此外,如果这失败了,在入口点,调试比你只是深入了解你的代码要容易得多,因为这样的失败只能意味着一件事:客户端没有向你发送所有必需的信息。在大多数情况下,您希望验证所有输入参数,如果您发现自己处于需要允许大量值的位置,则可能是接口设计不佳的迹象,需要重构/添加以满足客户端的需求。
Validate.notNull(someParam, "Can't function when someParam is null!");
IllegalArgumentException
NullPointerException
null
使用 s 时,返回一个空的而不是 null!
Collection
使用数据库时,请使用 -constraints。这样,您就会知道从数据库中读取的值不能为 null,并且不必检查它。
not null
构建您的代码并坚持下去。这样做可以对代码的行为做出假设,例如,如果应用程序的所有输入都经过验证,则可以假设这些值永远不会为 null。
如果您还没有这样做,请编写代码的自动测试。通过编写测试,您将对代码进行推理,并且您还将更加确信它能完成它应该做的事情。此外,自动化测试可以保护您在重构过程中避免出错,因为它会立即让您知道这段代码没有像以前那样做。
当然,您仍然需要进行 null 检查,但它可以减少到最低限度(即知道您可能会得到 null 值的情况,而不是为了确定而无处不在。当涉及到空检查时,我实际上更喜欢使用三元运算符(但要小心使用,当你开始嵌套它们时,它们会变得非常混乱。
public String nullSafeToString(final Object o) {
return o != null ? o.toString() : "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)
现在,您可以安全地开发方法的核心功能,而无需检查输入参数,它们可以保护您的方法免受意外参数的影响。
您可以更进一步,确保在应用程序中只能创建有效的 pojo。(来自 Hibernate 验证器站点的示例)
public class Car {
@NotNull
private String manufacturer;
@NotNull
@Size(min = 2, max = 14)
private String licensePlate;
@Min(2)
private int seatCount;
// ...
}
评论
javax
根据定义,它不是“核心 Java”。
我发现番石榴先决条件在这种情况下非常有用。我不喜欢将 null 留给 null 指针异常,因为理解 NPE 的唯一方法是找到行号。生产版本和开发版本中的行号可以不同。
使用 Guava 前提条件,我可以检查 null 参数并在一行中定义有意义的异常消息。
例如
Preconditions.checkNotNull(paramVal, "Method foo received null paramVal");
这是大多数开发人员最常遇到的错误。
我们有很多方法可以解决这个问题。
方法1:
org.apache.commons.lang.Validate //using apache framework
notNull(Object 对象,字符串消息)
方法2:
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;
}
评论
Objects.requireNonNull()
我完全无视建议在每种情况下使用 null 对象的答案。这种模式可能会破坏契约,将问题埋得越来越深,而不是解决问题,更不用说使用不当会产生另一堆需要未来维护的样板代码。
实际上,如果从方法返回的内容可以为 null,并且调用代码必须对此做出决定,则应该有一个较早的调用来确保状态。
还要记住,如果不小心使用,null 对象模式将占用大量内存。为此,NullObject 的实例应该在所有者之间共享,而不是每个所有者的单一实例。
此外,我不建议使用这种模式,其中类型是原始类型表示 - 就像数学实体一样,它们不是标量:向量、矩阵、复数和 POD(Plain Old Data) 对象,它们旨在以 Java 内置类型的形式保存状态。在后一种情况下,您最终会调用具有任意结果的 getter 方法。例如,NullPerson.getName() 方法应该返回什么?
为了避免荒谬的结果,值得考虑这种情况。
评论
对于每个 Java 开发人员来说,这是一个非常常见的问题。因此,Java 8 中提供了官方支持,可以在没有杂乱代码的情况下解决这些问题。
Java 8 引入了 .它是一个容器,可能包含也可能不包含非 null 值。Java 8 提供了一种更安全的方法来处理在某些情况下值可能为 null 的对象。它的灵感来自Haskell和Scala的思想。java.util.Optional<T>
简而言之,Optional 类包含显式处理值存在或不存在的情况的方法。但是,与 null 引用相比,优点是 Optional<T> 类强制您考虑值不存在时的情况。因此,可以防止意外的 null 指针异常。
在上面的示例中,我们有一个家庭服务工厂,该工厂将句柄返回给家庭中可用的多个设备。但这些服务可能可用,也可能不可用;这意味着它可能会导致 NullPointerException。让我们在使用任何服务之前不要添加 null 条件,而是将其包装到 Optional<Service> 中。if
包装到 OPTION<T>
让我们考虑一种从工厂获取服务引用的方法。不要返回服务引用,而是将其包装为 Optional。它让 API 用户知道返回的服务可能可用,也可能不可用/功能,防御性使用
public Optional<Service> getRefrigertorControl() {
Service s = new RefrigeratorService();
//...
return Optional.ofNullable(s);
}
如您所见,提供了一种简单的方法来包装引用。还有另一种方法可以获取 Optional 的引用,或者 & 。一个用于返回空对象而不是重新调整 null,另一个用于包装不可为 null 的对象。Optional.ofNullable()
Optional.empty()
Optional.of()
那么,究竟如何帮助避免空检查呢?
包装引用对象后,Optional 提供了许多有用的方法,用于在没有 NPE 的情况下调用包装引用上的方法。
Optional ref = homeServices.getRefrigertorControl();
ref.ifPresent(HomeServices::switchItOn);
如果给定的 Consumer 是非 null 值,则 Optional.ifPresent 使用引用调用给定的 Consumer。否则,它不执行任何操作。
@FunctionalInterface
public interface Consumer<T>
表示接受单个输入参数且不返回任何结果的操作。与大多数其他功能接口不同,消费者需要通过副作用来操作。
它是如此干净且易于理解。在上面的代码示例中,如果 Optional 持有引用为非 null,则调用。HomeService.switchOn(Service)
我们经常使用三元运算符来检查 null 条件并返回替代值或默认值。Optional 提供了另一种在不检查 null 的情况下处理相同条件的方法。Optional.orElse(defaultObj) 如果 Optional 具有 null 值,则返回 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 免检查代码......真的吗?
评论
if(homeServices != null) {
homeServices.ifPresent(h -> //action)
;
您可以使用 JUnit 等框架将类与单元测试耦合。 这样,您的代码将是干净的(没有无用的检查),并且您将确保您的实例不会为 null。
这是使用单元测试的一个很好的理由。
使用 Java 8 lambda 可以定义 util 方法,以几乎漂亮的方式处理嵌套的 null 检查。
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; }
}
(这个答案首先发布在这里。
Java 8 现在有 Optional 类,它包装了考虑的对象,如果存在值,isPresent() 将返回 true,get() 将返回该值。
http://www.oracle.com/technetwork/articles/java/java8-optional-2175753.html
我遵循以下准则以避免空检查。
尽可能避免成员变量的延迟初始化。初始化声明本身中的变量。这将处理 NullPointerExceptions。
在周期的早期确定成员变量的可变性。有效地使用关键字等语言结构。
final
如果您知道 method 的扩充不会更改,请将它们声明为 .
final
尽可能限制数据的突变。某些变量可以在构造函数中创建,并且永远无法更改。删除公共 setter 方法,除非它们确实是必需的。
例如,假设应用程序 () 中的一个类正在维护一个类似 的集合。不要在 A.java 中提供 getter 方法,并允许直接在 中添加元素。而是在 中提供一个 API,该 API 将元素添加到集合中。
A.java
HashMap
public
B.java
Map
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); }
最后,在正确的位置有效地使用积木。
try{} catch{} finally{}
对于 Java 8 或更高版本,最好的选择可能是使用 Optional
类。
Optional stringToUse = Optional.of("optional is there");
stringToUse.ifPresent(System.out::println);
这对于可能为 null 值的长链特别方便。例:
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();
在 Java 8 中,如果 local-variable/field/method-argument/method-return-type 从未赋值(并且不检查 ),则可以使用 type,如果可以,则可以使用 type。然后使用处理方法和处理方法:T
null
null
Optional<T>
null
map
T ->
flatMap
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;}
}
空对象模式可用作此问题的解决方案。为此,应修改 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
评论
object.doCalc()
doCalc()
由于该类存在。Java 7
java.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");
如果您使用的是 java8 或更高版本,请使用 from .isNull(yourObject)
java.util.Objects
例:-
String myObject = null;
Objects.isNull(myObject); //will return true
用法:下面的代码返回一个非 null 值(如果名称不为 null,则返回该值,否则将返回默认值)。
final String name = "Jobin";
String nonNullValue = Optional.ofNullable(name).orElse("DefaultName");
评论
myObject == null
Predicates
Objects
isNull
Objects.isNull
filter
Optional.ofNullable()
Optional.empty()
name
null
String nonNullValue = Optional.ofNullable(name).orElse("DefaultName");
Java 8 在 java.util 包中引入了一个新类 Optional。
Java 8 的优点 可选:
1.) 不需要空检查。
2.) 运行时不再出现 NullPointerException。
3.) 我们可以开发干净整洁的 API。
可选 - 容器对象,该对象可能包含也可能不包含非 null 值。如果存在值,isPresent() 将返回 true,get() 将返回该值。
有关更多详细信息,请在此处找到 oracle 文档:- https://docs.oracle.com/javase/8/docs/api/java/util/Optional.html
评论
Optionals
NPE
总而言之,避免陈述
if (object != null) {
....
}
从 Java 7 开始,您可以使用以下方法:
Objects
Objects.isNull(对象)
Objects.nonNull(对象)
Objects.requireNonNull(对象)
Objects.equals(对象 1, 对象 2)
从 java 8 开始,您可以使用 Optional 类(何时使用)
object.ifPresent(obj -> ...);
爪哇 8
object.ifPresentOrElse(obj -> ..., () -> ...);
爪哇 9
依赖方法契约 (JSR 305) 并使用 Find Bugs。使用注释和 .此外,还提供先决条件。
@javax.annotation.Nullable
@javax.annotation.Nonnnul
前提条件.checkNotNull(对象);
在特殊情况下(例如,对于字符串和集合),您可以使用 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) //番石榴字符串
- 当你需要分配默认值时,当 null 使用 apache commons lang
公共静态对象 defaultIfNull(对象,对象 defaultValue)
评论
您拥有的一种选择
使用检查器框架对方法的@RequiresNonNull。例如,如果您调用一个带有 null 参数的注释方法,则会得到此结果。它会在编译过程中失败,甚至在代码运行之前!因为在运行时它将是 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
在 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();
}
}
评论
你可以通过遵循问题的大多数其他答案来避免很多,我只想添加一些在 Java 9
中引入的方法,以优雅地处理这种情况,并展示一些旧的方法也可以使用,从而减少你的工作量。NullPointerException
public static boolean isNull(Object obj)
如果提供的引用为 null,则返回 true,否则返回 假。
从 Java 1.8 开始
public static boolean nonNull(Object obj)
如果提供的引用为非 null,则返回 true,否则返回 假。
从 Java 1.8 开始
public static <T> T requireNonNullElse(T obj, T defaultObj)
如果第一个参数为非 null,则返回该参数,否则返回 非 null 第二个参数。
从 Java 9 开始
public static <T> T requireNonNullElseGet(T obj, Supplier<? extends T> supplier)
如果第一个参数为非 null,则返回该参数,否则返回 supplier.get() 的非 null 值。
从 Java 9 开始
public static <T> T requireNonNull(T obj, Supplier<String> messageSupplier)
检查指定的对象引用是否不为 null,否则将引发自定义的 NullPointerException。
从 Java 1.8 开始
有关上述功能的更多详细信息,请参见此处。
评论
Java 9
具有 null 安全性的 Kotlin 是优雅的替代方案,但这意味着更大的变化。
函数式方法可能有助于包装重复的 null 检查并执行匿名代码,如以下示例所示。
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;} );
!= null 检查的另一种选择是(如果您无法在设计上摆脱它):
Optional.ofNullable(someobject).ifPresent(someobject -> someobject.doCalc());
或
Optional.ofNullable(someobject).ifPresent(SomeClass::doCalc);
其中 SomeClass 是某个对象的类型。
但是,您无法从 doCalc() 中获取返回值,因此仅对 void 方法有用。
评论
map
get
ifPresent
Java 8 在包中引入了一个新类 Optional。它用于表示值存在或不存在。这种新结构的主要优点是不再有太多的 null 检查和 .它避免了任何运行时,并支持我们开发干净整洁的 Java API 或应用程序。与 和 一样,它也是一个最多保存一个值的容器。java.util
NullPointerException
NullPointerExceptions
Collections
arrays
以下是您可以点击的一些有用链接
https://www.mkyong.com/java8/java-8-optional-in-depth/
https://dzone.com/articles/java-8-optional-avoid-null-and
您可以为对象和字符串创建一个通用方法,以便您可以在应用程序中使用它 - 这可以帮助您和您的同事: 创建一个类,例如。StringUtilities 并添加方法,例如。getNullString
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
}
在 Java 中避免 Null 检查的最佳方法是正确处理和使用异常。根据我的经验,随着您更接近前端,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!");
};
但这取决于你。
评论
!myvar
myvar.length()
!
有一种很好的方法可以检查 JDK 的 null 值。 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 真的非常有用。
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 包含 null 值,str2 为空字符串。
我们还创建了一个函数 isNullOrEmpty(),顾名思义,它检查字符串是 null 还是空。它使用 != null 和字符串的 isEmpty() 方法使用 null 检查来检查它。
简单来说,如果字符串不是 null 并且 isEmpty() 返回 false,则它不是 null 或空。否则,它是。
但是,如果字符串仅包含空格字符(空格),则上述程序不会返回空。从技术上讲,isEmpty() 看到它包含空格并返回 false。对于带空格的字符串,我们使用字符串方法 trim() 来修剪所有前导和尾随空格字符。
Objects.isNull(null)
如果您使用的是 Java8,那么您可以尝试此代码。
如果您不使用 Java8,请尝试使用以下代码
Object ob=null;
if(ob==null){ **do something}
就个人而言,我要么接受 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 中检查 null。
问题来自语言语法本身,一个对象总是可以是类型化或空的。甚至 Optional 对象也可以为 null。
所有其他建议(注释、可选等)只是围绕问题添加了更多的样板代码,但从未真正解决过它。更糟糕的是,手动处理的复杂性和极端情况会增加。
唯一可行的解决方案是不要自己编写样板,而让另一个工具为您完成。那是您使用 Kotlin 的时候。当他们设计语言时,空安全性是一个主要问题。
此外,Java 语法永远不会发生如此深刻的变化,因为它将意味着一些重大的突破性变化,实际上创造了一种新语言(kotlin 在最后已经做到了)。
这就是最近许多公司转向 kotlin 的主要原因。查看一些有关Facebook,Google android的文章...
而Java世界的一个主要问题是,很多人很久以前就学会了Java,以至于他们在情感上依恋它,不想改变他们的习惯。因此,他们试图证明他们奇怪和过时的模式是合理的,而不仅仅是使用元语言,比如前端世界的 JavaScript TypeScript。
可以通过以下方式处理:
import org.apache.commons.lang.ObjectUtils;
if(!ObjectUtils.equals(x, null)){
}
评论
final