避免!=空语句
在Java编程时,我使用最多的惯例是在使用它之前testingobject != null
。 这是为了避免NullPointerException 。 我发现代码非常难看,而且变得不可读。
有没有一个很好的select呢?
如果你想访问这个对象的字段或方法,我想要解决testing每个对象的必要性。
例如:
if (someobject != null) { someobject.doCalc(); }
在这种情况下,我将避免一个NullPointerException
,我不知道对象是否为null
。 作为结果,这些testing出现在我的代码中。
对我而言,这听起来像是一个相当普遍的问题,初级到中级开发人员在某些时候往往会面对这样的问题:他们要么不知道,要么不信任他们正在参与的合同,并且防御性地检查空值。 另外,在编写自己的代码时,他们倾向于依靠返回的空值来指示某些事情,因此需要调用者检查空值。
换句话说,有两个实例出现空检查:
-
凡无效是合同有效的答复; 和
-
哪里不是有效的答复。
(2)很容易。 使用assert
语句(断言)或允许失败(例如, NullPointerException )。 断言是1.4中添加的一个使用率很低的Java特性。 语法是:
assert <condition>
要么
assert <condition> : <object>
其中<condition>
是一个布尔expression式, <object>
是其toString()
方法的输出将包含在错误中的对象。
如果条件不成立, assert
语句会抛出一个Error
( AssertionError
)。 默认情况下,Java会忽略断言。 您可以通过将选项-ea
传递给JVM来启用断言。 您可以启用和禁用个别类和包的声明。 这意味着您可以在开发和testing的同时使用断言来validation代码,并在生产环境中禁用它们,尽pipe我的testing显示接近断言的性能影响。
在这种情况下不使用断言是可以的,因为代码将会失败,如果使用断言就会发生这种情况。 唯一的区别是,断言可能会更快地发生,以更有意义的方式,并可能有额外的信息,这可能会帮助你弄清楚为什么如果你不期待它发生。
(1)有点困难。 如果你无法控制你打电话的代码,那么你就被卡住了。 如果null是有效的响应,则必须检查它。
如果它是你所控制的代码,那么情况往往是这样的,那么情况就不一样了。 避免使用空值作为回应。 使用返回集合的方法,很简单:几乎总是返回空集合(或数组)而不是空值。
非collections可能会更困难。 考虑这个例子:如果你有这些接口:
public interface Action { void doSomething(); } public interface Parser { Action findAction(String userInput); }
在这里Parser需要原始的用户input,并find要做的事情,也许如果你正在实现一个命令行界面的东西。 现在,如果没有适当的操作,您可以使合同返回null。 这导致你正在谈论的空检查。
另一种解决scheme是永远不会返回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();
这是一个更好的devise,因为它导致更简洁的代码。
也就是说,对于findAction()方法来说,抛出一个有意义的错误信息的Exception是完全合适的 – 特别是在这种情况下,你依赖于用户input。 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); } } }
如果您使用(或计划使用) JetBrains IntelliJ IDEA (Java IDE),则可以使用由它们开发的一些特定的注释。
基本上,你有@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
实现的支持。
查看博客文章更灵活和可configuration的@ Nullable / @NotNull注释 。
如果不允许空值
如果你的方法是从外部调用的,就从这样的事情开始:
public void method(Object object) { if (object == null) { throw new IllegalArgumentException("..."); }
然后,在该方法的其余部分,您将知道该object
不是空的。
如果它是一个内部方法(不是API的一部分),只需logging它不能为null,就是这样。
例:
public String getFirst3Chars(String text) { return text.subString(0, 3); }
但是,如果你的方法只是传递值,下一个方法通过它等等,它可能会有问题。 在这种情况下,你可能想要像上面那样检查参数。
如果允许null
这真的取决于。 如果发现我经常这样做:
if (object == null) { // something } else { // something else }
所以我分支,做两个完全不同的事情。 没有丑陋的代码片断,因为我真的需要根据数据做两件不同的事情。 例如,我应该在input上工作,还是应该计算一个很好的默认值?
我真的很less使用这个成语“ if (object != null && ...
”。
如果你展示你通常使用习惯用法的例子,给你举例可能会更容易一些。
哇,当我们有57种不同的方式推荐NullObject pattern
时,我几乎不愿意添加另一个答案,但是我认为有些对这个问题感兴趣的人可能想知道,在Java 7上有一个提议将“null安全处理“ – 为if-not-equal-null逻辑简化语法。
Alex Miller给出的例子如下所示:
public String getPostcode(Person person) { return person?.getAddress()?.getPostcode(); }
那个?.
意味着只有在左标识符不为空的情况下才会引用左标识符,否则将其余expression式评估为null
。 像Java Posse成员Dick Wall和Devoxx的选民一样,真的很喜欢这个提议,但是也有反对意见,理由是实际上会鼓励更多地使用null
作为定位值。
更新: Java 7中的一个空安全运算符的正式提议已经在Project Coin下提交。 语法与上面的例子有点不同,但是它是相同的概念。
更新:无效运营商提案没有把它变成项目投币。 所以,你将不会在Java 7中看到这个语法。
如果未定义值不被允许:
您可能会configuration您的IDE以警告您有关潜在的空解除引用。 例如在Eclipse中,请参阅首选项> Java>编译器>错误/警告/空分析 。
如果允许未定义的值:
如果你想定义一个新的API,其中未定义的值是合理的 ,使用选项模式 (可能是function语言熟悉)。 它具有以下优点:
- 在API中明确地陈述input或输出是否存在。
- 编译器强制你处理“未定义”的情况。
- 选项是monad ,所以不需要进行详细的null检查,只需使用map / foreach / getOrElse或类似的组合器来安全地使用该值(示例) 。
Java 8有一个内置的Optional
类(推荐); 对于较早的版本,有库替代品,例如番石榴的Optional
或function性 Java Option
。 但是像许多function样式的模式一样,在Java中使用Option(甚至是8)会产生相当多的样板,您可以使用不太冗长的JVM语言(例如Scala或Xtend)来减less样式。
如果你必须处理一个可能返回空值的API ,那么在Java中你不能做太多事情。 Xtend和Groovy有Elvis操作符 ?:
和无效的解除引用操作符 ?.
,但是请注意,在空引用的情况下,这将返回null,所以它只是“推迟”了对null的正确处理。
只适用于这种情况 – 避免在string比较之前检查null:
if ( foo.equals("bar") ) { // ... }
如果foo
不存在,将导致NullPointerException
。
你可以避免,如果你比较你的String
是这样的:
if ( "bar".equals(foo) ) { // ... }
Java 8引入了新的java.util.Optional
类,可以解决一些问题。 人们至less可以说,它提高了代码的可读性,在公共API的情况下,使API的合约更清楚的给客户端开发者。
他们这样工作:
创build一个给定types( Fruit
)的可选对象作为方法的返回types。 它可以是空的或包含一个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实例中search一个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");
当然,对空值/空值的检查仍然是必要的,但至less开发者意识到价值可能是空的,忘记检查的风险是有限的。
在从零开始构build的API中,只要返回值可能为空,并且仅在不能为null
(惯例)时才返回纯对象,则客户机代码可能会放弃对简单对象返回值的空检查。
当然, Optional
也可以用作方法参数,在某些情况下,可能是指示可选参数比5或10重载方法更好的方法。
Optional
提供其他方便的方法,例如允许使用默认值的ifPresent
,以及与lambdaexpression式一起使用的ifPresent
。
我邀请你阅读这篇文章(我写这个答案的主要来源),其中NullPointerException
(以及一般的空指针)有问题以及由Optional
带来的(部分)解决scheme都得到了很好的解释: Java可选对象 。
根据你正在检查什么样的对象,你可以使用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类只是其中的一个; 在普通人中有相当多的好几类做无效的安全操作。
下面是一个如何在JAVA中使用null vallidation的例子,当你包含apache库(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的包中也有相同的function,参见library(spring-2.4.6.jar)
关于如何从spring中使用这个静态类的示例(org.springframework.util.Assert)
Assert.notNull(validationEventHandler,"ValidationHandler not Injected");
- 如果你认为一个对象不应该是null(或者是一个bug),那么使用一个断言。
- 如果你的方法不接受null参数在javadoc中说,并使用断言。
只有当你想处理对象可能为空的情况下,你必须检查对象!= null。
有一个build议,在Java7中添加新的注释来帮助null / notnull参数: http ://tech.puredanger.com/java7/#jsr308
我是“快速失败”代码的粉丝。 问问自己 – 在参数为空的情况下,你是否正在做一些有用的事情? 如果在这种情况下,你的代码应该做的没有明确的答案,即它不应该是空的,然后忽略它,并允许引发NullPointerException。 调用代码与NPE一样,会和IllegalArgumentExceptionexception一样,但开发人员debugging和理解如果抛出NPE而不是代码尝试执行其他意外的意外事件逻辑 – 最终导致应用程序失败。
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();
有时候,你有一些方法可以根据定义对称操作的参数进行操作:
af(b); <-> bf(a);
如果你知道b不能为空,你可以把它换掉。 这对于equals是最有用的:而不是foo.equals("bar");
更好地做"bar".equals(foo);
。
而不是空对象模式 – 有它的用途 – 你可能会考虑空对象是一个错误的情况。
抛出exception时,检查堆栈跟踪并处理该错误。
Java 7有一个新的java.util.Objects
工具类,其中有一个requireNonNull()
方法。 如果它的参数为null,所有这些都会抛出一个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"); }
空不是一个“问题”。 It is an integral part of a complete modeling tool set. Software aims to model the complexity of the world and null bears its burden. Null indicates 'No data' or 'Unknown' in Java and the like. So it is appropriate to use nulls for these purposes. I don't prefer the 'Null object' pattern; I think it rise the ' who will guard the guardians ' problem.
If you ask me what is the name of my girlfriend I'll tell you that I have no girlfriend. In the Java language I'll return null. An alternative would be to throw meaningful exception to indicate some problem that can't be (or don't want to be) solved right there and delegate it somewhere higher in the stack to retry or report data access error to the user.
-
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 */
要么
/** * @return photo, never null */
-
This is rather theoretical case and in most cases you should prefer java null safe API, but
NullPointerException
is subclass of anException
. Thus it is a form ofThrowable
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 catchNullPointerException
.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 ifgetPhotoByName()
would try to open a database connection, createPreparedStatement
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 whenThreadDeath
is anError
not because it is actually an application error, but solely because it was not intended to be caught! How much NPE fits to be anError
thanThreadDeath
! 但事实并非如此。 -
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); } }
和
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.
Ultimately, the only way to completely solve this problem is by using a different programming language:
- In Objective-C, you can do the equivalent of invoking a method on
nil
, and absolutely nothing will happen. This makes most null checks unnecessary, but it can make errors much harder to diagnose. - In Nice , a Java-derived language, there are two versions of all types: a potentially-null version and a not-null version. You can only invoke methods on not-null types. Potentially-null types can be converted to not-null types through explicit checking for null. This makes it much easier to know where null checks are necessary and where they aren't.
Common "problem" in Java indeed.
First, my thoughts on this:
I consider that it is bad to "eat" something when NULL was passed where NULL isn't a valid value. If you're not exiting the method with some sort of error then it means nothing went wrong in your method which is not true. Then you probably return null in this case, and in the receiving method you again check for null, and it never ends, and you end up with "if != null", etc..
So, IMHO, null must be a critical error which prevents further execution (that is, where null is not a valid value).
The way I solve this problem is this:
First, I follow this convention:
- All public methods / API always check its arguments for null
- All private methods do not check for null since they are controlled methods (just let die with nullpointer exception in case it wasn't handled above)
- The only other methods which do not check for null are utility methods. They are public, but if you call them for some reason, you know what parameters you pass. This is like trying to boil water in the kettle without providing water…
And finally, in the code, the first line of the public method goes like this:
ValidationUtils.getNullValidator().addParam(plans, "plans").addParam(persons, "persons").validate();
Note that addParam() returns self, so that you can add more parameters to check.
Method validate()
will throw checked ValidationException
if any of the parameters is null (checked or unchecked is more a design/taste issue, but my ValidationException
is checked).
void validate() throws ValidationException;
The message will contain the following text if, for example, "plans" is null:
" Illegal argument value null is encountered for parameter [plans] "
As you can see, the second value in the addParam() method (string) is needed for the user message, because you cannot easily detect passed-in variable name, even with reflection (not subject of this post anyway…).
And yes, we know that beyond this line we will no longer encounter a null value so we just safely invoke methods on those objects.
This way, the code is clean, easy maintainable and readable.
Asking that question points out that you may be interested in error handling strategies. Your team's architect should decide how to work errors. 有几种方法可以做到这一点:
-
allow the Exceptions to ripple through – catch them at the 'main loop' or in some other managing routine.
- check for error conditions and handle them appropriately
Sure do have a look at Aspect Oriented Programming, too – they have neat ways to insert if( o == null ) handleNull()
into your bytecode.
In addition to using assert
you can use the following:
if (someobject == null) { // Handle null here then move on. }
This is slightly better than:
if (someobject != null) { ..... ..... ..... }
Just don't ever use null. Don't allow it.
In my classes, most fields and local variables have non-null default values, and I add contract statements (always-on asserts) everywhere in the code to make sure this is being enforced (since it's more succinct, and more expressive than letting it come up as an NPE and then having to resolve the line number, etc.).
Once I adopted this practice, I noticed that the problems seemed to fix themselves. You'd catch things much earlier in the development process just by accident and realize you had a weak spot.. and more importantly.. it helps encapsulate different modules' concerns, different modules can 'trust' each other, and no more littering the code with if = null else
constructs!
This is defensive programming and results in much cleaner code in the long run. Always sanitize the data, eg here by enforcing rigid standards, and the problems go away.
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; } }
The contracts are like mini-unit tests which are always running, even in production, and when things fail, you know why, rather than a random NPE you have to somehow figure out.
Guava, a very useful core library by Google, has a nice and useful API to avoid nulls. I find UsingAndAvoidingNullExplained very helpful.
As explained in the wiki:
Optional<T>
is a way of replacing a nullable T reference with a non-null value. An Optional may either contain a non-null T reference (in which case we say the reference is "present"), or it may contain nothing (in which case we say the reference is "absent"). It is never said to "contain null."
用法:
Optional<Integer> possible = Optional.of(5); possible.isPresent(); // returns true possible.get(); // returns 5
I like articles from Nat Pryce. Here are the links:
- Avoiding Nulls with Polymorphic Dispatch
- Avoiding Nulls with "Tell, Don't Ask" Style
In the articles there is also a link to a Git repository for a Java Maybe Type which I find interesting, but I don't think it alone could decrease the checking code bloat. After doing some research on the Internet, I think != null code bloat could be decreased mainly by careful design.
This is a very common problem for every Java developer. So there is official support in Java 8 to address these issues without cluttered code.
Java 8 has introduced java.util.Optional<T>
. It is a container that may or may not hold a non-null value. Java 8 has given a safer way to handle an object whose value may be null in some of the cases. It is inspired from the ideas of Haskell and Scala .
In a nutshell, the Optional class includes methods to explicitly deal with the cases where a value is present or absent. However, the advantage compared to null references is that the Optional<T> class forces you to think about the case when the value is not present. As a consequence, you can prevent unintended null pointer exceptions.
In above example we have a home service factory that returns a handle to multiple appliances available in the home. But these services may or may not be available/functional; it means it may result in a NullPointerException. Instead of adding a null if
condition before using any service, let's wrap it in to Optional<Service>.
WRAPPING TO OPTION<T>
Let's consider a method to get a reference of a service from a factory. Instead of returning the service reference, wrap it with Optional. It lets the API user know that the returned service may or may not available/functional, use defensively
public Optional<Service> getRefrigertorControl() { Service s = new RefrigeratorService(); //... return Optional.ofNullable(s); }
As you see Optional.ofNullable()
provides an easy way to get the reference wrapped. There are another ways to get the reference of Optional, either Optional.empty()
& Optional.of()
. One for returning an empty object instead of retuning null and the other to wrap a non-nullable object, respectively.
SO HOW EXACTLY IT HELPS TO AVOID A NULL CHECK?
Once you have wrapped a reference object, Optional provides many useful methods to invoke methods on a wrapped reference without NPE.
Optional ref = homeServices.getRefrigertorControl(); ref.ifPresent(HomeServices::switchItOn);
Optional.ifPresent invokes the given Consumer with a reference if it is a non-null value. Otherwise, it does nothing.
@FunctionalInterface public interface Consumer<T>
Represents an operation that accepts a single input argument and returns no result. Unlike most other functional interfaces, Consumer is expected to operate via side-effects. It is so clean and easy to understand. In the above code example, HomeService.switchOn(Service)
gets invoked if the Optional holding reference is non-null.
We use the ternary operator very often for checking null condition and return an alternative value or default value. Optional provides another way to handle the same condition without checking null. Optional.orElse(defaultObj) returns defaultObj if the Optional has a null value. Let's use this in our sample code:
public static Optional<HomeServices> get() { service = Optional.of(service.orElse(new HomeServices())); return service; }
Now HomeServices.get() does same thing, but in a better way. It checks whether the service is already initialized of not. If it is then return the same or create a new New service. Optional<T>.orElse(T) helps to return a default value.
Finally, here is our NPE as well as null check-free code:
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){ //... } }
The complete post is NPE as well as Null check-free code … Really? 。
I've tried the NullObjectPattern
but for me is not always the best way to go. There are sometimes when a "no action" is not appropiate.
NullPointerException
is a Runtime exception that means it's developers fault and with enough experience it tells you exactly where is the error.
Now to the answer:
Try to make all your attributes and its accessors as private as possible or avoid to expose them to the clients at all. You can have the argument values in the constructor of course, but by reducing the scope you don't let the client class pass an invalid value. If you need to modify the values, you can always create a new object
. You check the values in the constructor only once and in the rest of the methods you can be almost sure that the values are not null.
Of course, experience is the better way to understand and apply this suggestion.
Byte!
May I answer it more generally!
We usually face this issue when the methods get the parameters in the way we not expected (bad method call is programmer's fault). For example: you expect to get an object, instead you get a null. You expect to get an String with at least one character, instead you get an empty String …
So there is no difference between:
if(object == null){ //you called my method badly!
}
要么
if(str.length() == 0){ //you called my method badly again! }
They both want to make sure that we received valid parameters, before we do any other functions.
As mentioned in some other answers, to avoid above problems you can follow the Design by contract pattern. Please see http://en.wikipedia.org/wiki/Design_by_contract .
To implement this pattern in java, you can use core java annotations like javax.annotation.NotNull or use more sophisticated libraries like Hibernate Validator .
Just a sample:
getCustomerAccounts(@NotEmpty String customerId,@Size(min = 1) String accountType)
Now you can safely develop the core function of your method without needing to check input parameters, they guard your methods from unexpected parameters.
You can go a step further and make sure that only valid pojos could be created in your application. (sample from hibernate validator site)
public class Car { @NotNull private String manufacturer; @NotNull @Size(min = 2, max = 14) private String licensePlate; @Min(2) private int seatCount; // ... }
- Never initialise variables to null.
- If (1) is not possible, initialise all collections and arrays to empty collections/arrays.
Doing this in your own code and you can avoid != null checks.
Most of the time null checks seem to guard loops over collections or arrays, so just initialise them empty, you won't need any null checks.
// 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 } }
There is a tiny overhead in this, but it's worth it for cleaner code and less NullPointerExceptions.
I highly disregard answers that suggest using the null objects in every situation. This pattern may break the contract and bury problems deeper and deeper instead of solving them, not mentioning that used inappropriately will create another pile of boilerplate code that will require future maintenance.
In reality if something returned from a method can be null and the calling code has to make decision upon that, there should an earlier call that ensures the state.
Also keep in mind, that null object pattern will be memory hungry if used without care. For this – the instance of a NullObject should be shared between owners, and not be an unigue instance for each of these.
Also I would not recommend using this pattern where the type is meant to be a primitive type representation – like mathematical entities, that are not scalars: vectors, matrices, complex numbers and POD(Plain Old Data) objects, which are meant to hold state in form of Java built-in types. In the latter case you would end up calling getter methods with arbitrary results. For example what should a NullPerson.getName() method return?
It's worth considering such cases in order to avoid absurd results.
This is the most common error occurred for most of the developers.
We have number of ways to handle this.
Approach 1:
org.apache.commons.lang.Validate //using apache framework
notNull(Object object, String message)
Approach 2:
if(someObject!=null){ // simply checking against null }
Approach 3:
@isNull @Nullable // using annotation based validation
Approach 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; }
Probably the best alternative for Java 8 or newer is to use the Optional
class.
Optional stringToUse = Optional.of("optional is there"); stringToUse.ifPresent(System.out::println);
This is especially handy for long chains of possible null values. 例:
Optional<Integer> i = Optional.ofNullable(wsObject.getFoo()) .map(f -> f.getBar()) .map(b -> b.getBaz()) .map(b -> b.getInt());
Example on how to throw exception on null:
Optional optionalCarNull = Optional.ofNullable(someNull); optionalCarNull.orElseThrow(IllegalStateException::new);
Java 7 introduced the Objects.requireNonNull
method which can be handy when something should be checked for non-nullness. 例:
String lowerVal = Objects.requireNonNull(someVar, "input cannot be null or empty").toLowerCase();
public static <T> T ifNull(T toCheck, T ifNull) { if (toCheck == null) { return ifNull; } return toCheck; }