我用object != null
了很多东西来避免NullPointerException
.
有没有一个很好的替代品呢?
例如:
if (someobject != null) { someobject.doCalc(); }
NullPointerException
当不知道对象是否存在时,这避免了a null
.
请注意,接受的答案可能已过期,请参阅/sf/ask/17360801/以获取更新的方法.
这对我来说听起来像是一个相当普遍的问题,初级到中级开发人员往往会在某些时候面临这样的问题:他们要么不知道,要么不信任他们参与的合同,并且防御性地过度检查空值.此外,在编写自己的代码时,它们倾向于依赖返回空值来指示某些内容,从而要求调用者检查空值.
换句话说,有两个实例进行空检查:
如果null是合同方面的有效回复; 和
哪里不是有效的回复.
(2)很容易.使用assert
语句(断言)或允许失败(例如, NullPointerException).断言是1.4中添加的高度未充分利用的Java功能.语法是:
assert
要么
assert:
where
是一个布尔表达式,是一个对象,其
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,而是使用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()方法完全适合抛出带有意义错误消息的异常 - 特别是在你依赖用户输入的情况下.对于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
实现的支持.
请参阅博客文章更灵活和可配置的@Nullable/@NotNull注释.
如果您的方法是外部调用的,请从以下内容开始:
public void method(Object object) { if (object == null) { throw new IllegalArgumentException("..."); }
然后,在该方法的其余部分中,您将知道它object
不是null.
如果它是一个内部方法(不是API的一部分),只需记录它不能为空,就是这样.
例:
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添加"null-safe"处理" -a-if-not-equal-null逻辑的简化语法.
Alex Miller给出的例子如下:
public String getPostcode(Person person) { return person?.getAddress()?.getPostcode(); }
该?.
装置仅去参考,如果它不为空左侧标识符,否则评估表达为其余部分null
.有些人,比如Java Posse成员Dick Wall和Devoxx的选民都非常喜欢这个提议,但也存在反对意见,理由是它实际上会鼓励更多地使用null
作为哨兵价值.
更新:已在Project Coin下提交了Java 7中零安全运算符的官方提议.语法与上面的示例略有不同,但它是相同的概念.
更新:零安全运营商提案未进入项目硬币.因此,您将不会在Java 7中看到此语法.
您可以配置IDE以警告您可能的空取消引用.例如,在Eclipse中,请参阅首选项> Java>编译器>错误/警告/空分析.
如果要定义未定义值有意义的新API,请使用选项模式(可能熟悉函数式语言).它具有以下优点:
在API中明确说明输入或输出是否存在.
编译器强制您处理"未定义"的情况.
Option是monad,因此不需要详细的null检查,只需使用map/foreach/getOrElse或类似的组合器来安全地使用该值(示例).
Java 8有一个内置Optional
类(推荐); 对于早期版本,有库的替代品,例如番石榴的Optional
或FunctionalJava的Option
.但是,与许多功能样式模式一样,在Java中使用Option(甚至8)会产生相当多的样板,使用较简洁的JVM语言(例如Scala或Xtend)可以减少这种样板.
如果你必须处理可能返回null的API,那么你在Java中做不了多少.Xtend和Groovy有Elvis运算符 ?:
和null-safe dereference运算符 ?.
,但请注意,如果是null引用,则返回null,因此它只是"延迟"null的正确处理.
仅限于这种情况 -
在调用equals方法之前不检查变量是否为null(下面的字符串比较示例):
if ( foo.equals("bar") ) { // ... }
将导致NullPointerException
if foo
不存在.
如果你比较你的String
s ,你可以避免这种情况:
if ( "bar".equals(foo) ) { // ... }
随着Java 8的出现,新的java.util.Optional
类可以解决一些问题.至少可以说它提高了代码的可读性,在公共API的情况下,使API的合同更清晰.
他们的工作方式如下:
Fruit
创建给定type()的可选对象作为方法的返回类型.它可以是空的或包含一个Fruit
对象:
public static Optionalfind(String name, List fruits) { for (Fruit fruit : fruits) { if (fruit.getName().equals(name)) { return Optional.of(fruit); } } return Optional.empty(); }
现在看一下我们搜索给定Fruit实例的Fruit
(fruits
)列表的代码:
Optionalfound = 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");
当然,检查空值/空值仍然是必要的,但至少开发人员意识到值可能是空的并且忘记检查的风险是有限的.
在从头开始构建的API中,Optional
只要返回值可能为空,并且只有当它不能null
(约定)时才返回普通对象,客户端代码可能会放弃对简单对象返回值的空检查...
当然Optional
也可以用作方法参数,在某些情况下,可能是指示可选参数而不是5或10重载方法的更好方法.
Optional
提供其他方便的方法,例如orElse
允许使用默认值,并ifPresent
使用lambda表达式.
我邀请你阅读这篇文章(我写这个答案的主要来源),其中NullPointerException
(和一般的空指针)问题以及带来的(部分)解决方案Optional
都得到了很好的解释:Java可选对象.
根据您检查的对象类型,您可以使用apache公共中的一些类,例如:apache commons lang和apache commons collections
例:
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(或者它是一个bug),请使用一个断言.
如果你的方法不接受null params在javadoc中说它并使用断言.
只有当你想要处理对象可能为null的情况时,你必须检查对象!= null ...
有人建议在Java7中添加新的注释来帮助null/notnull params:http://tech.puredanger.com/java7/#jsr308
我是"快速失败"代码的粉丝.问问自己 - 在参数为null的情况下,您是否正在做一些有用的事情?如果你没有一个明确的答案,你的代码在这种情况下应该做什么...即它首先应该永远不为null,然后忽略它并允许抛出NullPointerException.调用代码与IllegalArgumentException一样对NPE有同样的意义,但是如果抛出NPE而不是代码试图执行其他意外的意外事件,开发人员将更容易调试并理解出现了什么问题.逻辑 - 最终导致应用程序失败.
Google集合框架提供了一种实现空检查的优雅方式.
像这样的库类中有一个方法:
staticT 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();
有时,您有一些方法可以对其参数进行操作,从而定义对称操作:
a.f(b); <-> b.f(a);
如果你知道b永远不能为null,你可以只交换它.它对equals最有用:而不是foo.equals("bar");
更好"bar".equals(foo);
.
而不是Null对象模式 - 它有其用途 - 你可能会考虑null对象是一个bug的情况.
抛出异常时,检查堆栈跟踪并完成错误.
Null不是'问题'.它是完整建模工具集的组成部分.软件旨在模拟世界的复杂性,而null承担其负担.Null表示 Java等中的"无数据"或"未知".因此,为这些目的使用null是合适的.我不喜欢'Null对象'模式; 我认为它会提升 ' 谁将保护监护人 '的问题.
如果你问我女朋友的名字我会告诉你我没有女朋友.在Java语言中,我将返回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编写三个额外的单词.对于那些不阅读文档的人来说,这将是绰绰有余的.
/** * @return photo or null */
要么
/** * @return photo, never null */
这是理论上的情况,在大多数情况下,你应该更喜欢java null safe API(如果它将在另外10年内发布),但它NullPointerException
是一个的子类Exception
.因此,它的一种形式Throwable
表明合理的应用程序可能想要捕获的条件(javadoc)!为了使用异常的第一个最大优势和从"常规"代码中分离错误处理代码(根据Java的创建者),对我来说,抓住它是合适的NullPointerException
.
public Photo getGirlfriendPhoto() { try { return appContext.getPhotoDataSource().getPhotoByName(me.getGirlfriend().getName()); } catch (NullPointerException e) { return null; } }
可能会出现问题:
问:如果getPhotoDataSource()
返回null会怎么样?
答:这取决于业务逻辑.如果我找不到相册,我会告诉你没有照片.如果未初始化appContext怎么办?这种方法的业务逻辑就是这样.如果相同的逻辑应该更严格,那么抛出异常它就是业务逻辑的一部分,应该使用null的显式检查(情况3).在新的Java空安全的API更适合的位置,指定选择性意味着什么,什么不意味着被初始化是快速失败的编程错误的情况下.
问:可以执行冗余代码并且可以获取不必要的资源.
答:如果getPhotoByName()
尝试打开数据库连接,最后创建PreparedStatement
并使用人名作为SQL参数,则可能会发生这种情况.针对未知问题的方法给出了一个未知的答案(案例1).在获取资源之前,该方法应检查参数并在需要时返回"未知"结果.
问:由于尝试关闭开放,这种方法具有性能损失.
答:软件应该易于理解和修改.只有在此之后,才能考虑性能,并且只在需要时才考虑!并在需要的地方!(来源)和许多其他人).
PS.这种方法使用起来也是合理的,因为"常规"代码原则的单独错误处理代码在某些地方使用是合理的.考虑下一个例子:
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.对于那些快速下载(而不是那么快阅读文档)的人,我想说我的生活中从未发现空指针异常(NPE).但这种可能性是由Java创建者故意设计的,因为NPE是其子类Exception
.我们在Java历史上有一个先例,当时ThreadDeath
它Error
不是因为它实际上是一个应用程序错误,而仅仅是因为它不打算被抓住!多少NPE适合成为一个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将打印stacktrace到系统错误.应该监视应用程序错误日志并为每个未处理的异常打开Jira问题,这实际上是应用程序错误.程序员应该在初始化的东西中修复bug.
Java 7有一个新的java.util.Objects
实用程序类,其中有一个requireNonNull()
方法.所有这一切都是抛出一个NullPointerException
如果它的参数为null,但它会稍微清理一下代码.例:
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"); }
最终,完全解决此问题的唯一方法是使用不同的编程语言:
在Objective-C中,你可以相当于调用一个方法nil
,绝对不会发生任何事情.这使得大多数空检查变得不必要,但它可以使错误更难以诊断.
在Nice是一种Java派生语言,所有类型都有两个版本:潜在的null版本和非null的版本.您只能在非null类型上调用方法.通过显式检查null,可以将可能为空的类型转换为非null类型.这使得更容易知道哪些空检查是必要的,哪些不是.
Java中常见的"问题"确实如此.
首先,我对此的看法:
我认为在传递NULL时"吃"某些东西是不好的,其中NULL不是有效值.如果您没有以某种错误退出该方法,那么这意味着您的方法没有出错,这是不正确的.然后你可能在这种情况下返回null,并且在接收方法中你再次检查null,它永远不会结束,你最终得到"if!= null"等.
因此,IMHO,null必须是一个严重的错误,它会阻止进一步执行(即null不是有效值).
我解决这个问题的方法是这样的:
首先,我遵循这个惯例:
所有公共方法/ API总是检查其参数是否为null
所有私有方法都不检查null,因为它们是受控方法(如果上面没有处理,只需使用nullpointer异常)
唯一不检查null的其他方法是实用程序方法.它们是公开的,但是如果你出于某种原因打电话给它们,你就知道你传递了什么参数.这就像试图在没有提供水的情况下在水壶中煮水...
最后,在代码中,public方法的第一行如下:
ValidationUtils.getNullValidator().addParam(plans, "plans").addParam(persons, "persons").validate();
请注意,addParam()返回self,因此您可以添加更多参数进行检查.
如果任何参数为null,validate()
则抛出方法将检查ValidationException
(选中或取消选中更多是设计/品味问题,但我ValidationException
的选中).
void validate() throws ValidationException;
例如,如果"plans"为null,则该消息将包含以下文本:
" 参数[plans]遇到非法参数值null "
正如您所看到的,用户消息需要addParam()方法(字符串)中的第二个值,因为您无法轻松检测传入的变量名称,即使使用反射(无论如何都不是此帖子的主题......).
是的,我们知道超出这一行我们将不再遇到空值,所以我们只是安全地调用这些对象的方法.
这样,代码就干净,易于维护和读取.
问这个问题指出你可能对错误处理策略感兴趣.您的团队的架构师应该决定如何处理错误.做这件事有很多种方法:
允许异常波动 - 在'主循环'或其他一些管理例程中捕获它们.
检查错误情况并适当处理它们
当然也要看看面向方面编程 - 他们有简洁的方法插入if( o == null ) handleNull()
你的字节码.
除了使用之外,assert
您还可以使用以下内容:
if (someobject == null) { // Handle null here then move on. }
这略好于:
if (someobject != null) { ..... ..... ..... }
只是不要使用null.不要允许它.
在我的类中,大多数字段和局部变量都具有非空的默认值,并且我在代码中的任何地方添加了契约语句(always-on asserts)以确保执行它(因为它更简洁,更具表现力而不是让它作为NPE出现,然后必须解决行号等).
一旦我采用了这种做法,我注意到问题似乎已经解决了.你会在开发过程中更早地发现事情,并意识到你有一个弱点......更重要的是......它有助于封装不同模块的问题,不同的模块可以互相"信任",不再乱扔垃圾代码与if = null else
构造!
这是防御性编程,从长远来看会产生更清晰的代码.始终清理数据,例如通过强制执行严格的标准,问题就会消失.
class C { private final MyType mustBeSet; public C(MyType mything) { mustBeSet=Contract.notNull(mything); } private String name = ""; public void setName(String s) { name = Contract.notNull(s); } } class Contract { public static T notNull(T t) { if (t == null) { throw new ContractException("argument must be non-null"); return t; } }
合同就像迷你单元测试一样,即使在生产中也一直在运行,当事情失败时,你知道为什么,而不是随机的NPE,你必须以某种方式弄清楚.
Guava是Google非常有用的核心库,它有一个很好的有用的API来避免空值.我发现UsingAndAvoidingNullExplained非常有帮助.
如维基中所述:
Optional
是一种用非空值替换可空T引用的方法.Optional可以包含非空T引用(在这种情况下我们说引用是"存在"),或者它可以不包含任何内容(在这种情况下我们说引用是"不存在").它永远不会被称为"包含空".
用法:
Optionalpossible = Optional.of(5); possible.isPresent(); // returns true possible.get(); // returns 5
这对每个Java开发人员来说都是一个非常普遍的问题 因此,Java 8中有官方支持来解决这些问题,而不会出现混乱的代码.
Java 8已经推出java.util.Optional
.它是一个容器,可能包含也可能不包含非null值.Java 8提供了一种更安全的方法来处理在某些情况下其值可能为null的对象.它的灵感来自Haskell和Scala的想法.
简而言之,Optional类包括明确处理存在或不存在值的情况的方法.但是,与null引用相比的优点是Optional
在上面的示例中,我们有一个家庭服务工厂,它返回家中可用的多个设备的句柄.但是这些服务可能有用,也可能没有用; 这意味着它可能导致NullPointerException.不要if
在使用任何服务之前添加空条件,而是将其包装到Optional
包装选项
让我们考虑一种从工厂获取服务引用的方法.而不是返回服务引用,请使用Optional包装它.它允许API用户知道返回的服务可能或可能不可用/可用,防御性地使用
public OptionalgetRefrigertorControl() { Service s = new RefrigeratorService(); //... return Optional.ofNullable(s); }
如您所见,Optional.ofNullable()
提供了一种简单的方法来获取引用.还有另一种方法可以获得Optional的引用,或者Optional.empty()
&Optional.of()
.一个用于返回空对象而不是重新调整null,另一个用于包装不可为空的对象.
所以它如何帮助避免空检查?
一旦包装了引用对象,Optional就会提供许多有用的方法来在没有NPE的情况下调用包装引用上的方法.
Optional ref = homeServices.getRefrigertorControl(); ref.ifPresent(HomeServices::switchItOn);
Optional.ifPresent如果它是非null值,则使用引用调用给定的Consumer.否则,它什么都不做.
@FunctionalInterface public interface Consumer
表示接受单个输入参数但不返回结果的操作.与大多数其他功能接口不同,消费者需要通过副作用进行操作.它非常干净,易于理解.在上面的代码示例中,HomeService.switchOn(Service)
如果Optional保留引用为非null ,则调用gets.
我们经常使用三元运算符来检查空状态并返回替代值或默认值.Optional提供了另一种处理相同条件的方法,而不检查null.如果Optional具有空值,则Optional.orElse(defaultObj)返回defaultObj.我们在示例代码中使用它:
public static Optionalget() { service = Optional.of(service.orElse(new HomeServices())); return service; }
现在HomeServices.get()做了同样的事情,但是以更好的方式.它检查服务是否已初始化.如果是,则返回相同或创建新的新服务.可选
最后,这是我们的NPE以及无空检查代码:
import java.util.Optional; public class HomeServices { private static final int NOW = 0; private static Optionalservice; public static Optional get() { service = Optional.of(service.orElse(new HomeServices())); return service; } public Optional getRefrigertorControl() { Service s = new RefrigeratorService(); //... return Optional.ofNullable(s); } public static void main(String[] args) { /* Get Home Services handle */ Optional homeServices = HomeServices.get(); if(homeServices != null) { Optional refrigertorControl = homeServices.get().getRefrigertorControl(); refrigertorControl.ifPresent(HomeServices::switchItOn); } } public static void switchItOn(Service s){ //... } }
完整的帖子是NPE以及Null免检代码......真的吗?.
我喜欢Nat Pryce的文章.以下是链接:
用多态调度避免空值
用"告诉,不要问"风格避免空虚
在文章中还有一个指向Java Maybe Type的Git存储库的链接,我发现它很有趣,但我不认为它可以减少检查代码膨胀.在对互联网进行一些研究之后,我认为!=空代码膨胀主要是通过精心设计来减少的.
我试过了,NullObjectPattern
但对我来说并不总是最好的方式.有时候"不采取行动"是不恰当的.
NullPointerException
是一个运行时异常,这意味着它是开发人员的错误,并且有足够的经验它会告诉您确切的错误位置.
现在回答:
尽量使所有属性及其访问者尽可能保密,或者避免将它们暴露给客户端.当然,您可以在构造函数中包含参数值,但是通过减小范围,您不会让客户端类传递无效值.如果需要修改值,可以随时创建新值object
.您只检查构造函数中的值一次,在其余的方法中几乎可以确定值不为null.
当然,经验是理解和应用此建议的更好方式.
字节!
Java 8或更高版本的最佳替代方法可能是使用Optional
该类.
Optional stringToUse = Optional.of("optional is there"); stringToUse.ifPresent(System.out::println);
对于可能为空的值的长链,这尤其方便.例:
Optionali = 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
一种方法,当检查某些内容是否为非null时,该方法很方便.例:
String lowerVal = Objects.requireNonNull(someVar, "input cannot be null or empty").toLowerCase();
我可以更一般地回答它!
当方法以我们不期望的方式获取参数时,我们通常会遇到这个问题(错误的方法调用是程序员的错误).例如:您希望获得一个对象,而不是获得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)
现在,您可以安全地开发方法的核心功能,而无需检查输入参数,它们可以保护您的方法免受意外参数的影响.
您可以更进一步,确保只能在您的应用程序中创建有效的pojos.(来自hibernate验证器站点的示例)
public class Car { @NotNull private String manufacturer; @NotNull @Size(min = 2, max = 14) private String licensePlate; @Min(2) private int seatCount; // ... }
我高度忽略了建议在每种情况下使用null对象的答案.这种模式可能会破坏合同并将问题深入和深入地埋没而不是解决它们,没有提到使用不当会产生另一堆样板代码,需要将来维护.
实际上,如果从方法返回的内容可以为null并且调用代码必须对此做出决定,则应该有一个确保状态的早期调用.
还要记住,如果毫无使用,那么空对象模式将是内存饥饿.为此 - 应该在所有者之间共享NullObject的实例,而不是每个实例的unigue实例.
此外,我不建议使用此模式,其中类型是基本类型表示 - 如数学实体,不是标量:向量,矩阵,复数和POD(普通旧数据)对象,它们用于保持状态以Java内置类型的形式.在后一种情况下,您最终会调用具有任意结果的getter方法.例如,NullPerson.getName()方法应返回什么?
值得考虑这些案例以避免荒谬的结果.
永远不要将变量初始化为null.
如果(1)不可能,则将所有集合和数组初始化为空集合/数组.
在您自己的代码中执行此操作,您可以避免!= null检查.
大多数情况下,null检查似乎保护集合或数组的循环,因此只需将它们初始化为空,就不需要任何空检查.
// Bad ArrayListlemmings; String[] names; void checkLemmings() { if (lemmings != null) for(lemming: lemmings) { // do something } } // Good ArrayList lemmings = new ArrayList (); String[] names = {}; void checkLemmings() { for(lemming: lemmings) { // do something } }
这有一个很小的开销,但是对于更干净的代码和更少的NullPointerExceptions来说它是值得的.
这是大多数开发人员最常见的错误.
我们有很多方法可以解决这个问题.
方法1:
org.apache.commons.lang.Validate //using apache framework
notNull(Object object,String message)
方法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 staticT isNull(someObject e){ if(e == null){ throw new NullPointerException(); } return e; }
public staticT ifNull(T toCheck, T ifNull) { if (toCheck == null) { return ifNull; } return toCheck; }
Java 8在java.util包中引入了一个新类Optional.
Java 8的优点可选:
1.)不需要空检查.
2.)在运行时不再有NullPointerException.
3.)我们可以开发干净整洁的API.
可选 - 容器对象,可能包含也可能不包含非null值.如果存在值,则isPresent()将返回true,get()将返回该值.
有关详细信息,请参阅此处oracle docs: - https://docs.oracle.com/javase/8/docs/api/java/util/Optional.html
您可以使用FindBugs.他们还有一个Eclipse插件,可以帮助您找到重复的空值检查(除其他外),但请记住,有时您应该选择防御性编程.还有Java合同可能会有所帮助.
总而言之,要避免陈述
if (object != null) { .... }
从java 7开始你可以使用Objects
方法:
Objects.isNull(对象)
Objects.nonNull(对象)
Objects.requireNonNull(对象)
Objects.equals(object1,object2)
从java 8开始你可以使用Optional类(何时使用)
object.ifPresent(obj -> ...);
java 8
object.ifPresentOrElse(obj -> ..., () -> ...);
java 9
依赖方法契约(JSR 305)并使用Find Bugs.与标记注释你的代码@javax.annotation.Nullable
和@javax.annotation.Nonnnul
.还有先决条件.
Preconditions.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)// Guava Strings
当你需要在null时使用apache commons lang分配默认值
public static Object defaultIfNull(Object object,Object defaultValue)
我按照以下指南来避免空检查.
尽可能避免成员变量的延迟初始化.初始化声明本身中的变量.这将处理NullPointerExceptions.
在周期的早期确定成员变量的可变性.final
有效地使用关键字等语言结构.
如果您知道不会更改方法的扩充,请将它们声明为final
.
尽可能限制数据的变异.某些变量可以在构造函数中创建,永远不会更改.删除公共setter方法,除非确实需要它们.
例如,假设application(A.java
)中的一个类正在维护一个类似的集合HashMap
.不要public
在A.java中提供getter方法并允许B.java
直接添加元素Map
.而是提供一个API 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 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");
NullPointerException
通过仅仅关注问题的大部分其他答案,你可以避免最多的避免,我只想添加一些已经引入的方法 Java 9
来优雅地处理这个场景,并且还展示了一些较旧的方法也可以使用,从而减少你的努力.
public static boolean isNull(Object obj)
如果提供的引用为null,则返回true,否则返回false.
自Java 1.8以来
public static boolean nonNull(Object obj)
如果提供的引用为非null,则返回true,否则返回false.
自Java 1.8以来
public static
如果它是非null,则返回第一个参数,否则返回非null的第二个参数.
自Java 9起
public static
如果它是非null,则返回第一个参数,否则返回supplier.get()的非null值.
自Java 9起
public static
检查指定的对象引用是否为null,否则抛出自定义的NullPointerException.
自Java 1.8以来
关于上述功能的更多细节可以在这里找到.
还有一个选择:
以下简单的函数有助于隐藏空检查(我不知道为什么,但我没有发现它是同一个公共库的一部分):
public staticboolean isNull(T argument) { return (argument == null); }
你现在可以写了
if (!isNull(someobject)) { someobject.doCalc(); }
这是IMO更好的表达方式!= null
.
无论您传递数组还是Vector,只需将其初始化为空数,而不是null. - 这样你可以避免大量的空检查,一切都很好:)
public class NonNullThing { Vector vectorField = new Vector(); int[] arrayField = new int[0]; public NonNullThing() { // etc } }
在这种情况下,我发现Guava Preconditions非常有用.我不喜欢将空值留给空指针异常,因为了解NPE的唯一方法是找到行号.生产版本和开发版本中的行号可以不同.
使用Guava Preconditions,我可以检查空参数并在一行中定义有意义的异常消息.
例如,
Preconditions.checkNotNull(paramVal, "Method foo received null paramVal");