我使用 x != null
来避免 NullPointerException
。有其他的替代方法吗?
if (x != null) {
// ...
}
我使用 x != null
来避免 NullPointerException
。有其他的替代方法吗?
if (x != null) {
// ...
}
有时候,你拥有一些对参数进行对称操作的方法:
a.f(b); <-> b.f(a);
如果您知道b永远不可能为null,那么您可以直接交换它。 这对于equals方法非常有用:
与其使用foo.equals("bar");
,不如使用"bar".equals(foo);
。
equals
(可以是任何方法) 正确处理 null。实际上,所有这些都只是将责任转移到其他人(或另一种方法)。 - Supericyequals
方法(或其他方法)必须检查 null
。或者明确说明它不检查。 - Angelo Fuchs与其使用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();
For an 'unknown question' give 'unknown answer'. (Be null-safe where this is correct from business point of view) Checking arguments for null once inside a method before usage relieves multiple callers from checking them before a call.
public Photo getPhotoOfThePerson(Person person) {
if (person == null)
return null;
// Grabbing some resources or intensive calculation
// using person object anyhow.
}
Previous leads to normal logic flow to get no photo of a non-existent girlfriend from my photo library.
getPhotoOfThePerson(me.getGirlfriend())
And it fits with new coming Java API (looking forward)
getPhotoByName(me.getGirlfriend()?.getName())
While it is rather 'normal business flow' not to find photo stored into the DB for some person, I used to use pairs like below for some other cases
public static MyEnum parseMyEnum(String value); // throws IllegalArgumentException
public static MyEnum parseMyEnumOrNull(String value);
And don't loathe to type <alt> + <shift> + <j>
(generate javadoc in Eclipse) and write three additional words for you public API. This will be more than enough for all but those who don't read documentation.
/**
* @return photo or null
*/
or
/**
* @return photo, never null
*/
This is rather theoretical case and in most cases you should prefer java null safe API (in case it will be released in another 10 years), but NullPointerException
is subclass of an Exception
. Thus it is a form of Throwable
that indicates conditions that a reasonable application might want to catch (javadoc)! To use the first most advantage of exceptions and separate error-handling code from 'regular' code (according to creators of Java) it is appropriate, as for me, to catch NullPointerException
.
public Photo getGirlfriendPhoto() {
try {
return appContext.getPhotoDataSource().getPhotoByName(me.getGirlfriend().getName());
} catch (NullPointerException e) {
return null;
}
}
Questions could arise:
Q. What if getPhotoDataSource()
returns null?
A. It is up to business logic. If I fail to find a photo album I'll show you no photos. What if appContext is not initialized? This method's business logic puts up with this. If the same logic should be more strict then throwing an exception it is part of the business logic and explicit check for null should be used (case 3). The new Java Null-safe API fits better here to specify selectively what implies and what does not imply to be initialized to be fail-fast in case of programmer errors.
Q. Redundant code could be executed and unnecessary resources could be grabbed.
A. It could take place if getPhotoByName()
would try to open a database connection, create PreparedStatement
and use the person name as an SQL parameter at last. The approach for an unknown question gives an unknown answer (case 1) works here. Before grabbing resources the method should check parameters and return 'unknown' result if needed.
Q. This approach has a performance penalty due to the try closure opening.
A. Software should be easy to understand and modify firstly. Only after this, one could think about performance, and only if needed! and where needed! (source), and many others).
PS. This approach will be as reasonable to use as the separate error-handling code from "regular" code principle is reasonable to use in some place. Consider the next example:
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;
}
PPS. For those fast to downvote (and not so fast to read documentation) I would like to say that I've never caught a null-pointer exception (NPE) in my life. But this possibility was intentionally designed by the Java creators because NPE is a subclass of Exception
. We have a precedent in Java history when ThreadDeath
is an Error
not because it is actually an application error, but solely because it was not intended to be caught! How much NPE fits to be an Error
than ThreadDeath
! But it is not.
Check for 'No data' only if business logic implies it.
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);
}
}
and
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);
}
If appContext or dataSource is not initialized unhandled runtime NullPointerException will kill current thread and will be processed by Thread.defaultUncaughtExceptionHandler (for you to define and use your favorite logger or other notification mechanizm). If not set, ThreadGroup#uncaughtException will print stacktrace to system err. One should monitor application error log and open Jira issue for each unhandled exception which in fact is application error. Programmer should fix bug somewhere in initialization stuff.
if (maybeNull.hasValue()) {...}
的代码,那么与if (maybeNull != null)) {...}
有什么区别呢? - MikeMaybe<T>
或Optional<T>
的好处不在于T
可能为空的情况,而是在于它永远不应该为空的情况。如果你有一个类型明确表示“这个值可能为空——谨慎使用”,并且你一直使用和返回这样的类型,那么无论何时在你的代码中看到一个普通的T
,就可以假设它永远不为null。(当然,如果编译器可以强制执行,这将更加有用。) - cHaoObjects.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)
。 - Stuart MarksdoCalc()
的作者,并且它在给定null时没有立即抛出NPE,那么您需要检查null并自己抛出NPE。这就是Objects.requireNonNull()
的作用。 - Stuart MarksObjects.requireNonNull(object, string)
版本(其中您可以将错误消息作为第二个参数传递)比单参数版本更有用(后者可能只是为了完整性而添加),特别是当您使用它来检查多个参数值时。假设您必须检查 3 个参数是否为空,这将使您的代码从 3 个 if/then-throw 块(由空行分隔)减少到 3 个不需要空行的单行。 - Cornel Masson最终,彻底解决这个问题的唯一方法是使用不同的编程语言:
nil
上调用一个方法,而绝对什么都不会发生。这使得大多数空值检查是不必要的,但也会使错误的诊断变得更加困难。在 Java 中这确实是一个普遍的问题。
首先,我对此的想法是:
当 NULL 不是一个有效值时,"吃掉"传入的 NULL 是不好的。如果你没有使用某种错误退出方法,那么就意味着你的方法中没有出现任何问题,但事实并非如此。然后你可能会返回 null,在接收方法中再次检查 null,这个过程永无止境,最终会得到 "if != null" 等等代码...
因此,我认为 null 必须是一个关键的错误,它会阻止进一步的执行(也就是说,null 不是一个有效值的情况下)。
我解决这个问题的方式是:
首先,我遵循以下约定:
最后,在代码中,公共方法的第一行应该像这样:
ValidationUtils.getNullValidator().addParam(plans, "plans").addParam(persons, "persons").validate();
请注意,addParam()返回自身,这样您就可以添加更多要检查的参数。void validate() throws ValidationException;
如果例如"plans"为空,那么该消息将包含以下文本:提出这个问题表明您可能对错误处理策略感兴趣。如何和在哪里处理错误是一个普遍的架构问题。有几种方法可以解决这个问题。
我最喜欢的方法是允许异常传递 - 在“主循环”或其他具有适当职责的函数中捕获它们。检查错误条件并适当地处理它们可以被视为一种专业化的职责。
确实也要看看面向方面的编程(AOP) - 它们有巧妙的方法将if( o == null ) handleNull()
插入到您的字节码中。
除了使用 assert
,您还可以使用以下内容:
if (someobject == null) {
// Handle null here then move on.
}
这略微比以下内容更好:
if (someobject != null) {
.....
.....
.....
}
if (!something) { x(); } else { y(); }
,我会倾向于将其重构为if (something) { y(); } else { x(); }
(尽管有人可能会认为!= null
是更积极的选项...)。但更重要的是,代码的重要部分没有被包裹在{}
中,对于大多数方法来说,你可以少一层缩进。我不知道这是否是fastcodejava的想法,但这是我的想法。 - Tyler永远不要使用null。不要允许它存在。
在我的课程中,大多数字段和局部变量都具有非null的默认值,并且我在代码中添加契约语句(始终开启断言),以确保这一点得到执行(因为它比让其成为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。