我经常使用object != null 来避免NullPointerException 。
有没有更好的选择?
例如:
1 2 3
if ( someobject != null ) {
someobject.doCalc ( ) ;
}
当不知道对象是否为null 时,避免使用NullPointerException 。
请注意,接受的答案可能已过时,请参阅https://stackoverflow.com/a/2386013/12943了解最新的方法。
@汤姆·霍丁:至少在那里讨论过:tech.puredanger.com/java7/空,也许他们最终决定不实施它。我不确定…
@夏尔文谈了很多事情。我想提案应该是三位数。但我认为它从来没有打算把这个捡起来。
@汤姆·霍丁:真是太糟糕了。我觉得这是个好主意。当然,我知道这会降低代码的可读性…
@舍文鼓励空值使得代码不容易理解和不可靠。
埃尔维斯运算符被提出,但看起来它不会在Java 7中。太糟糕了??还有?[]是令人难以置信的省时工具。
不使用空值优于这里的大多数其他建议。抛出异常,不要返回或允许空值。btw-"assert"关键字是无用的,因为它在默认情况下是禁用的。使用始终启用的故障机制
老实说,他们需要在Java中包含埃尔维斯操作符。这会使事情变得更容易
你看过谷歌的番石榴图书馆吗?code.google.com/p/guava图书馆/wiki/…
我找到了处理这个问题的最佳文章:sw engineering candies.com/blog-1/…
我们可以在哪里投票吗?OpenJDK.Java.NET/PIPEMail/BEN DEV/ 2009三月/ 000047。
可以创建将检查空值或空对象的类。这将帮助您提高重用能力。stackoverflow.com/a/16833309/1490962
使用lombok,你得到@notnull
这就是我现在使用scala的原因之一。在scala中,所有内容都不能为空。如果您希望允许传递或返回"Nothing",那么您必须隐式地使用选项[T],而不是仅使用T ALS参数或返回类型。
有点不相关,但这是imo scala的一个最佳特性:选项[t]。太棒了。
@事实上,斯卡拉的选择是非常棒的,但要注意:在斯卡拉,一切都可以是空的!
@事实上,scala的令人敬畏的Option 类型受到了语言不愿意控制或拒绝null 的破坏。我在HackerNews上提到这一点,被否决了,并说"在scala中没有人使用空值"。猜猜看,我已经在同事写的scala中找到了空值。是的,他们"做错事",必须接受教育,但事实仍然是,语言的类型系统应该保护我不受这种影响,而且它没有:(
为什么不搬到斯卡拉?(任选其一)
难道没有设计模式可以说明如何生成一个空对象吗?因此,在实例化一个新对象时,您总是使用这个空对象(相同的超级类,相同的[但为空]方法),然后在需要时将该对象设置为完整对象。您可能需要查看类似的问题:stackoverflow.com/questions/9162371/…以及有关空对象模式的信息:en.wikipedia.org/wiki/null_object_pattern
@桌面版的java.is.for.desktop,但是我们怎么知道舍文写了什么?
我经常写如果(某物==空)返回;
类似问题stackoverflow.com/questions/15518958/null-check-coding-stand‌&8203;ard/…
在5月8日Java中的EDCOX1 0类是有用的!
我对同样的情况感到厌烦。对我来说,我们应该有一个新的接线员:某个物体!DOCALC()。如果某个对象不为空,则只调用docalc,以运算符为例!(例如)。
我在读关于C 6.0的新闻,发现了空条件空操作符。ITs exactly my suggestion above, but it 不是!是吗?.
对我来说,null 在数学上和NaN 一样有用(不是数字,就像用0除以0)。
howtoonijava.com/2013/04/05/…
这就是我喜欢C的原因。C 6增加了? 运算符。你只需要写一封信就行了。
避免此类问题的最佳方法-只需使用正常/预期的流程开发代码。您应该快速失败,或者用防御性编程设计应用程序。
在高级代码中应该避免空值。发明了空引用的托尼·霍尔称之为"10亿美元的错误"。看看这里有什么想法。
似乎在Java 8中:静态对象。
只需编写一个帮助函数就可以了
用可选项包装对象怎么样?在看到空值的可能性时使用可选的
相对于其他被封闭得太广的问题,我读到的这个问题太广了。但这是一个很好的问题,有一些很好的答案。
haskell根本没有空值,您可以使用一个包装器作为一个好的选择。
对我来说,这听起来像是初级到中级开发人员在某一点上所面临的一个相当普遍的问题:他们要么不知道,要么不信任他们正在参与的合同,并在防御上过度检查空值。此外,在编写自己的代码时,它们往往依赖于返回空值来指示某些内容,从而要求调用者检查空值。
换言之,有两种情况会出现空检查:
如果"无效"是合同条款中的有效答复;以及
如果它不是有效的响应。
(2)容易。使用assert 语句(断言)或allow failure(例如nullpointerException)。断言是在1.4中添加的一个高度未充分使用的Java特征。语法是:
或
1
assert < condition> : < object>
其中 是一个布尔表达式, 是一个对象,其toString() 方法的输出将包括在错误中。
如果条件不是真的,assert 语句将抛出Error (AssertionError )。默认情况下,Java忽略断言。您可以通过将选项-ea 传递给JVM来启用断言。可以为单个类和包启用和禁用断言。这意味着您可以在开发和测试时使用断言来验证代码,并在生产环境中禁用它们,尽管我的测试几乎没有显示断言对性能的影响。
在这种情况下不使用断言是可以的,因为代码只会失败,如果使用断言,就会发生这种情况。唯一的区别是,对于断言,它可能以更有意义的方式发生得更快,而且可能会有额外的信息,这可能帮助您了解如果您没有预料到它会发生的原因。
(1)有点难。如果你无法控制你所呼叫的代码,那么你就被卡住了。如果空是有效的响应,则必须检查它。
但是,如果你控制的是代码(通常是这样),那么情况就不同了。避免使用空值作为响应。对于返回集合的方法,很容易:几乎总是返回空集合(或数组),而不是空集合。
对于非集合,这可能更难。举个例子:如果您有这些接口:
解析器获取原始用户输入并找到要做的事情,如果您正在为某件事情实现命令行接口的话。现在,如果没有适当的操作,您可以使契约返回空值。这导致了你所说的无效检查。
另一种解决方案是从不返回空值,而是使用空对象模式:
1 2 3 4 5 6 7 8 9 10 11 12
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
;
}
}
}
比较:
1 2 3 4 5 6 7 8 9 10 11
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 ( ) ;
}
到
1
ParserFactory.getParser ( ) .findAction ( someInput) .doSomething ( ) ;
这是一个更好的设计,因为它会导致代码更简洁。
也就是说,对于findaction()方法来说,抛出带有有意义的错误消息的异常可能是完全合适的,特别是在您依赖用户输入的情况下。对于findaction方法来说,引发异常比调用方法使用简单的nullPointerException而不进行解释要好得多。
1 2 3 4 5
try {
ParserFactory.getParser ( ) .findAction ( someInput) .doSomething ( ) ;
} catch ( ActionNotFoundException anfe) {
userConsole.err ( anfe.getMessage ( ) ) ;
}
或者,如果您认为尝试/捕获机制过于丑陋,而不是什么都不做,那么您的默认操作应该向用户提供反馈。
1 2 3 4 5 6 7 8
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
) ;
}
}
}
我不同意你关于"不做什么"行动的说法。如果find action方法找不到操作,则返回null是正确的操作。您在代码中"发现"了一个未被真正发现的操作,这违反了方法的原则,从而找到一个可用的操作。
我同意NULL在Java中被过度使用,特别是在列表中。如果返回空列表/数组/集合而不是空列表/数组/集合,那么许多API会更好。很多时候,在应该引发异常的地方使用空值。如果分析器无法分析,则应引发异常。
我认为在源代码中应该可以指定从方法x中永远不会返回空值,并且给定的参数不应该是空值,传递一个参数是错误的。
后面的例子是,IIRC,空对象设计模式。
断言有利于测试。在Prod上使用很危险。
我不同意什么都不做,因为尽管它消除了空检查,但结果行为是意外的。如果空是对方法的意外输入,那么您的方法抛出NPE以使调用方知道并记录事件(一种快速失败的行为)是相关的。使用Do-Nothing时,它就像传递了所有内容,但结果操作没有发生,用户将重新执行该操作(重新提交表单)。我同意返回空列表、对象,但不将空值作为返回值,但不同意上面所示的不做任何事情。但是NPE不应该在用户界面上显示stacktrace
@CSHAH(和Metroidfan2002)。简单地说,把它放入合同中,然后很明显,一个未找到的返回操作将什么都不做。如果这对调用者来说是重要的信息,那么提供一种方法来发现它是一个未找到的操作(即提供一种方法来检查结果是否是Do-Nothing对象)。或者,如果通常应该找到操作,那么您仍然不应该返回空值,而是抛出一个特别指示该条件的异常——这仍然会产生更好的代码。如果需要,请提供一个返回布尔值的单独方法,以检查操作是否存在。
我在编码时是防御性的,并执行空检查。尤其是在J2EE环境中。我的想法是任何可以为空的东西……在某个时刻都可能为空。事实上,在大多数情况下,我不想抛出一个ILLARALARG异常或NulLPosil,因为Java首先不应该有空引用。在大多数情况下,我试图找到一个默认的返回值,并以默认值/预期的返回值快速失败。
简洁并不等于质量代码。很抱歉你这么认为。您的代码隐藏了错误有利的情况。
@metroidfan,如果您返回一个有效的操作,而这个操作不做任何事情,那么您有一个更简单的没有分支的调用者。分支会导致额外的复杂性,并且尽可能少地保留它们会使测试更加简单。
@Thorbj&248;rn-Ravn-Andersen通过隐藏findaction方法可能找不到动作的可能性,现在您已经做了逻辑,虽然它不会破坏程序,但可能会导致不希望的效果,直到很晚的阶段(可能在生产阶段)才能找到。取决于findaction是否真的能被期望找不到任何东西,这是非常不希望的——返回空值或抛出异常,就像想象男孩所说的那样,是更好的方法,以便能够明确地检查它们,并考虑到找不到动作的路径。
当断言只对你失败时,使用它们有什么意义?是的,你可以在工作的时候抓到东西,但是在生产中,如果你在一些奇怪的情况下从来没有听说过,它们会有什么帮助呢?
检查空值没有什么错,我见过很多高级/rockstar开发人员在任何地方检查空值。此外,空检查是编程语言中最便宜的操作之一。
同意达特海德。我会说,没有足够的防御性是首要的事情。在我的经验中,很少有库,也很少有编码人员能够完全描述他们的契约(包括是否可以返回空值/参数的可接受值),因此通常没有多少值得信任的。
@SAJ:为什么断言只对测试有效,对生产系统有害?如果存在导致错误的条件,那么它也必须在生产中失败。然而,有一些JUnit测试或集成测试来检查这个断言是很重要的。此外,使用"-ea"启用断言
在"比较"代码中,您的观点被公开了,因为如果您在为action 返回NULL 时无所事事,那么为什么要检查NULL ?您使代码看起来比检查非NULL 值更糟糕。为了避免编码风格,我更喜欢在询问NULL 时看到的一些Android API代码。如果所涉及的方法无法继续工作,即使thought method profile返回void,它们也会返回。如果必须这样做,请在返回语句之前安排。当然,你正在返回并打破高级语言的正常流程。
断言对我来说是不可用的,因为必须显式地启用它——我希望始终启用断言。有人说:"但在生产中,如果你在一些奇怪的情况下从来没有听说过,他们会如何帮助?"简单-你可以得到一个精确的,清晰的故障,尽可能接近问题的根本原因,并且可以很容易地修补它,比反向工程整个系统,试图找出故障在哪里的努力要少得多。这就是为什么契约式设计,快速失败,没有空设计是唯一的方法……不要使用"断言",使用一个始终启用的机制….
我可能会创建一个hasaction(userinput)方法,而不是do-nothing操作,如果该userinput有一个有效的操作,则返回true或false。然后findaction将返回操作或引发异常actionNotFoundException
"尽管我的测试几乎没有显示断言对性能的影响"。您的假设并非基于Oracle文档。它对包含实时计算的系统类型的性能有很大的影响。
我同意Metroidfan2002。他很在行。有时会返回空值,检查空值一点也不脏。无论如何,如果您可以避免空值并总是初始化契约,那么您就不需要检查空值。
@Snorfus+1,用于说明空对象设计模式。我在这里学到了一些东西。参见en.wikipedia.org/wiki/null_object_pattern
我从不返回空值,所以我从不检查返回值是否为空。我从不需要考虑哪些变量是有效的。他们都是。我从不将空值传递给方法,因此我的方法中不需要任何输入验证。如果实际拥有一个允许传递空参数的方法是合乎逻辑的,那么我将创建两个方法。因此,通过打破公共区域来最小化代码重复非常重要。
我反对代码膨胀,同意这个解决方案。显式检查空值是一种反模式imho。Java方式是抛出NoDebug、无效访问等异常,避免了调用方显式处理不规则情况的必要性。我甚至更进一步:使用arg.equals("foo")而不是"foo"。equals(arg),或者使用本机类型(long而不是long)隐式强制NPE。幸运的是,当返回一个空对象时,调用者仍然可以尝试对该空值调用一个方法并强制NPE。- -阿尔-阿尔法)
通常,无论在何处接收空对象,如果对象为空,您希望实现的行为都是类似的。正如其他人所指出的,答案是"什么都不做",事实上,空对象模式(en.wikipedia.org/wiki/null-object-pattern)收集了这个"如果为空,我们该怎么做?"逻辑在一个地方,因此减少了重复。但是,空对象模式实际上只是MartinFowler的特殊情况模式(martinfowler.com/eaacatalog/specialcase.html)的一个更为有限的版本,这对于消除对空以外的值的检查也非常有用。
我在这里选择可选/可选或例外。返回空值对调用者来说是一种毒害;他们会忘记处理它,并且会在远离调用站点的地方看到问题。
@Metroidfan2002:同意回复:什么都不做,但不完全。如果find action方法找不到操作,那么该方法没有按照它所说的那样做。在这种情况下,它不应该返回。它应该扔。
@TravisWilson如果该方法要抛出某个东西,它应该抛出IllegalArgumentException,而不是NPE,因为它显式地检查空值并对其执行某些操作。IAE还将处理获取非空输入,并且无法为它找到任何东西。但是,现在使用Java 5 +,我将抛出NPE IF。该方法以EnUM常量作为参数,因为您已经有了(每个枚举常数之一),或者如果观察到枚举的正确实践,则什么也没有。
@用户1050755使用字符串所做的操作既危险又幼稚。当您对一个文本进行检查时,您希望知道该文本是否匹配,而不是字符串是否为空。这样做的幼稚方法正如您所建议的,带有一个无用的空检查:if(stringvar!=null&;stringvar.equals("literal"),但这会强制您直接发送空输入以测试覆盖率,从而导致不必要的分支。".equals(stringvar)将始终工作,不管。如果你知道要检查的字面意思,你应该一直这样做-这是最安全的,容易阅读,不做额外的条件。
我不明白为什么每个人都讨厌什么都不做。当然,在这个例子中,它是最低限度的-但这是一种编程语言…向类中添加"isEmptyAction()"方法…关键是要对对象的状态进行编码,而不是返回某种神秘的缺少值的值,而您必须知道这些值才能检查……"isEmptyAction()"是一个实际的API,它对操作可能为空/不执行任何操作这一事实进行编码。更干净的多,IMO(和许多其他,因此它是自己定义的设计模式…)
@Metroidfan2002:当然。NPE通常不是正确的选择。找不到方法是完全合法的结果,因此它应该是消费者需要了解的特定内容。可能是输入错误的结果,在这种情况下,我喜欢IllegalArgumentException(但对于它是RuntimeException并不感到高兴)。同样,根据这个规则:如果方法说它可能返回一个不做任何事情的操作(例如,为了消费者的方便),那么返回"不做任何事情"是可以的。
当某个不应该是空的东西时,抛出NPE是非常明智的事情。顺便说一句,如果处理并发性,应该小心,因为在检查时不为空的东西在使用时可能为空。
我刚刚在答案中添加了最后两个建议——Try/Catch和更有意义的默认操作。@我认为除非"someinput"为空,否则npe没有意义。我同意特拉维斯·威尔逊的观点,非法言论例外也是不理想的,尽管这并不合适。我的建议是一个自定义异常,其中包含一条有意义的消息,可以向提供错误输入的用户显示。
@Metroidfan2002年晚了。对象模式为空的点是返回空值。用户仍然可以通过将空对象与空对象进行比较来检查空对象。她也可以忽略这样一个事实:她得到了一个空值:这是优势。如果您实现了一个方法,并且您认为空值是一个可以返回的OK值,那么您返回的内容(空值)仍然应该遵守您承诺返回的内容的API。如果这是不可接受的,那么你应该放弃。用户仍然可以自己决定,检查空对象并抛出。
@Ziggy并不是说我不同意空对象模式,我一直在使用它。在这种特殊情况下,方法签名是findaction,一个do-nothing操作,根据定义,找不到!这是在骗你的用户。让用户检查Do-Nothing是否违反了多态性——现在并不是所有实例都可以互换使用。为了透视它,假设方法是bigdecimal findbigdecimal(string whatever)——这里不使用空对象模式。如果返回零,则发现某些东西对您是谎言。
空对象模式比null 更有用,因为有时结果不会立即使用——例如,它可以存储在字段中。然后,您可以区分由未初始化字段(可能是编程错误)引起的null ,与某些操作导致的空值。
警告:断言可以被禁用,并且在生产环境中被发现是被禁用的。
空对象模式只适用于简单的情况,因为Java缺少多重继承。不幸的是,空值是唯一可以代表任意对象的值,这就是为什么它被如此广泛地使用的原因。
当API"总是可用"是相关的(例如策略模式)时,我发现自己使用了空对象dp,但我通常发现自己只是抛出了冗长而有意义的异常。我不记得上一次明确地检查Java中的NULL(或者任何真正的OO语言),那么,我不经常用Java编写代码。我只想说:这是一次史诗般的谈话,也是一次非常有启发性的阅读。这就是我为什么这么做的原因。感谢大家参与。
在Java8中现在有一个更好的解决方案:使用可选的作为返回值来表示一个可以为空的值。这样,调用方就知道它是一个可选值,并且可以使用可选提供的所有方法来处理该值。
我喜欢什么都不做的行动,但似乎没有人提到抛出非法言论例外。如果论点无效,这似乎是一个很好的回答。无论哪种方式,该方法都需要有适当的文档。另外,@sprinter返回"不做任何事情"操作与可选操作非常相似。两者都有不返回空值的目标。不过,我不相信可选方案是"一个更好的解决方案"。
总的来说,问题不是开发人员不知道或不信任合同;问题是没有合同。根据我对几家硬件供应商发布的Android SDK的经验,既没有文档,也没有有用的文档。香港和班加罗尔的代码米尔斯甚至从来没有听说过JavaDoc。
当一个电子工程师变成程序员时,当我试图学习更多的xd时,这个话题让我头晕目眩。
仅仅因为有人给你的文件开启器传递了一个空的文件路径就抛出了一个错误,这是非常过分的。想象一下如果有人在应用服务器中使用它。
真正的问题是,何时空值是有效的响应,何时不有效。对于软件来说,什么是最不混乱的样板代码呢?
自从Java 8以来,我总是使用可选的这样的例子。从易用性的角度来看,在我看来这并不是什么大的区别,但它看起来更美观一些。所以我写的不是if (obj != null) ,而是if (objOpt.isPresent()) 。
@metroidfan2002添加到您的声明中-对nullObjectPattern的批评
我在生产中看到过太多愚蠢的nullpointer异常,以至于认为防御性地检查nulls是"初级到中级开发人员的问题",在我看来,总是执行空检查。
@metroidfan2002"如果find action方法找不到操作,则返回null是正确的操作",这完全取决于程序的设计者来决定不执行任何操作的操作是否适合他的用例。如果符合您的设计,返回一个不做任何事情的操作是完全有效的。您意味着真实/虚假的动作区分对调用者很重要,但是根据您的用例,这两种方法都是有效的。对于调用者来说,未找到的操作也很重要,但是可以通过返回空值以外的其他方式进行通信。
作为一个新到Java的人,我发现有趣的是,人们会发现这样做是有用的。为什么程序员不总是编写一个try 或catch 语句,并使用它来避免和解决错误?
本例的后半部分基本上不使用Java。虽然这可能是个好主意,但有时你没有选择。在Java中,NULL是一个有效的值。
一个查找方法打开了一种可能性,即它找不到我们要查找的内容。对于我来说,一个很好的习惯是对明显会失败的函数使用两个不同的动词:findxx可能会失败,因此可能返回空值或更好的选项,getxxx可能只会通过抛出异常而失败。我还看到了一些变体,比如find和find,或者find和find。不管怎样,只要使用正常,就可以了。
这是一个非常好的答案!
如果您使用(或计划使用)JaveIDE,如JETBEEL ITELLIJ想法、Eclipse或NETBeBes或像FunBug这样的工具,那么您可以使用注释来解决这个问题。
基本上,你有@Nullable 和@NotNull 。
您可以在方法和参数中使用,如下所示:
1 2 3
@NotNull
public static String helloWorld
( ) {
return "Hello World" ;
}
或
1 2 3
@Nullable
public static String helloWorld
( ) {
return "Hello World" ;
}
第二个例子不会编译(在intellij思想中)。
在另一段代码中使用第一个helloWorld() 函数时:
1 2 3 4 5 6 7
public static void main
( String [ ] args
)
{
String result
= helloWorld
( ) ;
if ( result
!= null ) {
System .
out .
println ( result
) ;
}
}
现在Intellij IDEA编译器会告诉您检查是无用的,因为helloWorld() 函数永远不会返回null 。
使用参数
1
void someMethod( @NotNull someParameter) { }
如果你写的是:
这无法编译。
最后一个使用@Nullable 的示例
1
@Nullable iWantToDestroyEverything( ) { return null ; }
这样做
1
iWantToDestroyEverything( ) .something ( ) ;
你可以确定这不会发生。:)
这是一种让编译器检查比平时更多的东西的好方法,而且可以加强合同的执行。不幸的是,并非所有编译器都支持它。
在Intellij IDEA 10.5及更高版本中,他们增加了对任何其他@Nullable @NotNull 实现的支持。
请参阅更灵活和可配置的博客帖子@nullable/@notnull注释。
@NotNull 、@Nullable 和其他空性注释是JSR 305的一部分。您还可以使用findbugs等工具来检测潜在的问题。
我发现@NotNull 和@Nullable 接口存在于com.sun.istack.internal 包中,这让人感到非常恼火。(我想我会将com.sun与使用专有API的警告关联起来。)
JetBrains的代码可移植性是空的。在绑定到IDE级别之前,我会三思而后行。就像Jacek S所说的,它们是JSR的一部分,顺便说一下,我认为它们是JSR303的一部分。
为什么第二个不编译呢?
我真的不认为使用自定义编译器是解决这个问题的可行方案。
注释的好处在于,当源代码由一个不理解它们的系统构建时,它们会很好地降级。因此,事实上,代码不可移植的论点可能是无效的——如果您使用支持和理解这些注释的系统,您将获得更严格的错误检查的额外好处,否则您将获得更少的错误检查,但您的代码仍应构建良好,并且您正在运行的程序的质量是相同的,因为这些注释在运行时不强制执行。此外,所有编译器都是自定义的;-)
在您的示例中,您两次调用helloWorld 。这太可怕了。你不知道会有什么后果!
@机械蜗牛因为它只会返回"你好世界",而且永远不会返回空值。
@Jaceks仅供参考-对于Android开发,自Android支持库的19.1版起,增加了@NotNull 和@Nullable 注释支持。所以基本上所有的Android开发者都可以利用它。它不再是特定于IDE的了。有关支持注释的更多信息,请访问:tools.android.com/tech-docs/support-annotations
反对@Nullable /@NotNull 的"兼容性"的论据缺少重点。它们只是用来帮助工具(从静态分析中)确保满足这些基本契约-它们不会替换适当的空处理,如果这对于契约是适当的(或者对于输入源是可能的)。如果Java和C语言(和运行时间)实际上支持类型系统中的非空引用类型,那么使用强大的静态分析工具(和显式注释的契约)来减少100兆美元的错误,这是非常棒的,这是Algo,Java,C是…
Netbeans还支持@NotNull 和@Nullable 。
值得注意的是,Type使用注释(Java 8)允许您将空注释视为类型系统的一个组成部分,包括特定的泛型。这意味着在概念上,编译器可以检测到所有的契约冲突。Eclipse支持这一点,因为Java 8发布了,请参阅帮助。Eclig.Org/Posith/org。Eclipse。JDT。doc。用户/任务/ & Helip;
第二个例子给出了什么错误消息(在intellij思想中)?
现在应该是主要答案了,我相信在这些注释可用之前,接受的答案已经被接受了。我想知道是否有一种方法可以让它晋升到最高层,至少……
@mwfearnley我相信第二个答案的注释是错误的,nullable更像是对开发人员的警告,我不认为编译器证明它可以是空的(因为重写的方法可以返回空)。它的真正含义是,如果没有空检查的保护,就不能将它传递给notnull。
顺便说一下,检查程序的实现并不都是一致的!Eclipse的Checker比Intellij好得多(尽管我一直在使用Intellij,但它肯定比什么都没有要好)。日食可以更好地处理未注释的变量。stackoverflow.com/questions/48349393/&hellip;
如果不允许空值
如果您的方法是从外部调用的,请从以下内容开始:
然后,在该方法的其余部分中,您将知道object 不是空的。
如果它是一个内部方法(不是API的一部分),只需记录它不能为空,就可以了。
例子:
1 2 3
public String getFirst3Chars
( String text
) {
return text.
subString ( 0 ,
3 ) ;
}
但是,如果您的方法只是传递值,而下一个方法传递值等等,那么它可能会有问题。在这种情况下,您可能需要如上所述检查参数。
如果允许空值
这要看情况而定。如果发现我经常这样做:
1 2 3 4 5
if ( object == null ) {
// something
} else {
// something else
}
所以我做了两件完全不同的事情。没有难看的代码片段,因为我真的需要根据数据做两件不同的事情。例如,我应该处理输入,还是应该计算一个好的默认值?
实际上,我很少用"EDOCX1"(3)这个成语。
如果你举例说明你通常在哪里使用这个习语,给你举个例子可能会更容易。
抛出IllegalArgumentException有什么意义?我认为nullpointerException会更清晰,如果您不亲自进行空检查,也会抛出这个异常。我要么使用断言,要么什么都不使用。
除了空值之外,其他所有值都不太可能被接受。你可以有非法的argumentexception,outofrageeexception等等,有时候这是有道理的。其他时候,您最终会创建许多不增加任何值的异常类,然后只使用IllegalArgumentException。对于空输入有一个异常,对于其他所有输入有另一个异常是没有意义的。
是的,我同意fail fast原则,但在上面给出的示例中,该值不是传递的,而是调用方法的对象。因此,它失败的速度相当快,并且添加一个空检查只是为了抛出一个异常,而这个异常无论如何都会在同一时间和同一地点抛出,这似乎并不能使调试变得更容易。
我同意@axel的观点,因为illegalargumentexception意味着(无论如何我都使用它),参数的前提条件不成立,因为它与参数的约束有关。空参数通常只是"编程中的一般错误",而不是特定于应用程序/API的约束。为了保持一种快速防御失败的策略,没有什么能阻止任何人检查空值并抛出空指针异常…它准确地传达了发生的事情,即,"您没有正确地编程":)
空参数通常只是"编程中的一般错误",而不是特定于应用程序/API的约束。您可以将其用作DoS的安全漏洞,或者尝试获取StackTrace信息(如果不支持此类信息)。osvdb.org/show/osvdb/67379如果您的API是公共的,并且您没有明确定义您接受的边界——通过API文档和任何可以用来确保合同有效性的工具——调用者可以自由地做他们想做的事情。你是那个没有编程的人,而不是来电者。
安全漏洞?JDK中充满了这样的代码。如果不希望用户看到stacktrace,只需禁用它们。没有人暗示这种行为没有记录在案。MySQL是用C语言编写的,在C语言中,取消对空指针的引用是未定义的行为,这与引发异常无关。
throw new IllegalArgumentException("object==null")
@MyPlacedk我认为对于传入的空值有一个不同于传入的不正确值的异常是有意义的。这两者通常是不同类型编程错误的结果。空值通常是由一些未发生的必需计算引起的,而其他问题通常是由不正确的必需计算引起的。
@axel使用runtimeexception以外的任何东西的关键是异常的描述性。为什么对NullPointerException执行IllegalArgumentException?因为在调试由此引起的异常时,IllegalArgumentException更加明显,尤其是当您在其上放置消息时。当nullpointerExceptions出现时,这尤其适用于企业应用程序(是的,这些都发生在企业应用程序中)。在这个相同的例子中,程序员可能无法控制断言是否由JVM处理。
1/2我必须同意,在这种情况下,IllegalArgumentException 比普通香草NPE 更好。我把NPE看作是代码中某个不安全的地方引用了一个恰好为空的表达式(假设满足所有已知和声明的前提条件和不变量)。非法的参数异常Oth告诉我一个众所周知的前提条件或不变量没有满足。
2/2——也就是说,一个NPE告诉我在方法中(或者在它调用的方法中)有一个尚未记录的空错误,或者有一个我们没有编码的空前提条件。一个非法的参数异常表明在这个方法的外部有一个bug,它位于导致当前方法的调用链的某个位置。坚持这种模式(显然是有原因的),它确实会加快错误的根本原因分析(特别是那些令人讨厌的、相互交织的错误)。
抛出EDCOX1×3是Java约定。参见例如Objects.requireNonNull ,它"主要用于方法和构造函数中的参数验证",并抛出NullPointerException 。还有一个简单的NullPointerException 文档,声明"应用程序应该抛出这个类的实例来指示null 对象的其他非法使用"。
哇,当我们有57种不同的方法来推荐EDCOX1的4度时,我几乎不喜欢添加另一个答案,但是我认为有些人对这个问题感兴趣可能想知道在JAVA 7的表上有一个建议添加"NULL安全处理"和MQASH;如果不是相等的空逻辑,则有一个简化的语法。
亚历克斯·米勒给出的例子如下:
1 2 3
public String getPostcode
( Person person
) {
return person
? .
getAddress ( ) ? .
getPostcode ( ) ;
}
?. 表示只有当左标识符不为空时才取消引用,否则将表达式的其余部分计算为null 。一些人,比如JavaPasSE成员Dick Wall和DeVox的选民们真的很喜欢这个提议,但也有反对意见,理由是它实际上会鼓励更多地使用EDOCX1 6作为前哨值。
更新:在Project Boin上提交了Java 7中的零安全操作员的官方建议。语法与上面的示例稍有不同,但它的概念相同。
更新:空的安全操作员建议没有进入Project Coin。所以,在Java 7中,你不会看到这种语法。
我认为这是错误的。应该有一种方法来指定给定变量总是非空的。
更新:该提案将不会生成Java7。参见blogs.sun.com/darcy/entry/project_coin_final_five。
但在同一页上,请参阅Cyth.C.WaseNut.EdU/JSR308与使用Cuffor框架在Java 7中添加"@空"和"非空类型注释"有关的信息的链接。
有趣的想法,但是语法的选择是荒谬的;我不希望在每个关节上都钉满问号的代码库。
这个操作符存在于groovy中,所以那些想要使用它的人仍然可以选择它。
目标C处理得很好:(狗吠声)如果狗是零,什么都不做。我理解把Java引入一个等价物会引起NIL指针增殖的担心,但是在这两个时间里都有了发展,我不得不说,目标C版本(和它的非抖动语法)是可取的。
旁白:同一个接线员来到c in.net5
在java8中添加了可选的
这是我见过的最巧妙的想法。它应该添加到C语法的每一种合理的语言中。我宁愿在任何地方都"钉上问号",也不愿意整天在屏幕上滚动行或躲避"保护条款"。
托尼·霍尔为发明null 而道歉。但是一个更大的错误是选择一个策略,坚持20多年直到数十亿行代码被写出来,然后最终改变你的想法。这个EDCOX1的1度是保持Java"干净"的最后机会。这是一个非常简单的语法解决方案。但相反,他们更喜欢放入一些不同的@Nullable 和Option 类、空集合、空对象。其目的是使空性更容易。但相反,他们增加了许多层次的复杂性。最后,这将拆分Java社区并将其驱动到DeStudio。
我不相信这个零安全操作符的缺失会破坏Java。事实上,巧合的是,自从这项提议被否决后,它在过去几年里变得越来越流行。
FFS为什么不实施?
@Shirkit也许他们觉得Optional 可以以更一般的方式覆盖这里的大多数用例。但是在我看来,为这个特殊的案例使用这样更好的语法可能是值得的。德米特定律也可能是一个因素;根据它,你真的"不应该"有这些长链的去引用。但实践和象牙塔的理想有时会出现分歧。
我们看不到的坏消息。
Kotlin也有空的安全调用运算符。kotlinlang.org/docs/reference/null safety.html安全调用
如果不允许未定义的值:
您可以将您的IDE配置为警告您潜在的空取消引用。例如,在Eclipse中,请参见首选项> java>编译器>错误/警告/ null分析。
如果允许未定义的值:
如果要定义一个新的API,其中未定义的值是有意义的,请使用选项模式(可能在函数语言中很熟悉)。它具有以下优点:
API中明确说明了输入或输出是否存在。
编译器强制您处理"未定义"的情况。
选项是monad,因此不需要进行详细的空检查,只需使用map/foreach/getorelse或类似的组合器来安全地使用该值(示例)。
Java 8有一个内置的EDCOX1×0的类(推荐);对于早期的版本,有库的替代品,例如番石榴的EDOCX1×0或OntalJava的EDCOX1(2)。但是,与许多功能样式模式一样,使用Java中的选项(甚至8)会产生相当多的样板,可以使用较少冗长的JVM语言(例如Scala或XCATEN)来减少。
如果你必须处理一个可能返回空值的API,那么在爪哇你做不了多少。xtend和groovy有elvis操作符?: 和NULL安全解引用操作符?. ,但请注意,如果引用为NULL,则返回NULL,因此它只是"延迟"对NULL的正确处理。
实际上,选项模式非常棒。一些Java等价物存在。番石榴含有一个称为可选的有限版本,它忽略了大部分功能性的东西。在Haskell中,这个模式被称为"可能"。
@卢卡·莫尔特尼:你说得对,我已经在帖子上评论过要求扩展它了。:)
一个可选的类将在Java 8中可用。
…而且它还没有地图或平面地图:download.java.net/jdk8/docs/api/java/util/optional.html
可选模式不能解决任何问题;现在您有了两个对象,而不是一个可能为空的对象。
另外,看看函数型的guava扩展(fugue)。这个图书馆也有自己的选择。
@博安,如果小心使用,你可以解决所有的NPES问题。如果没有,那么我想有一个"使用"问题。
在这种情况下,guava的Optional 并不好,因为当函数返回null 时,transform 抛出一个NPE。
只有在这种情况下-
在调用equals方法之前不检查变量是否为空(下面的字符串比较示例):
1 2 3
if ( foo.equals ( "bar" ) ) {
// ...
}
如果不存在foo ,将导致NullPointerException 。
如果您将您的String 比较如下,可以避免这种情况:
1 2 3
if ( "bar" .equals ( foo) ) {
// ...
}
我同意——只有在那种情况下。我不能忍受程序员把它带到下一个不必要的级别,然后写if(空!= MyVar)在我看来很难看,毫无意义!
这是一个特别的例子,可能是最常用的,一个一般的良好实践:如果你知道的话,一定要做.equals(); 。它适用于除equals 以外的其他方法,如果您知道合同,并且这些方法可以处理null 参数。
这是我看到的第一个有意义的尤达条件的例子。
出于某种原因引发了NullPointerExceptions。它们之所以被抛出,是因为对象在不应该出现的地方是空的。程序员的工作是解决这个问题,而不是隐藏问题。
试着抓住不知道隐藏的问题,这是一个有效的做法,以解决失踪糖的语言。
这只会隐藏问题。如果您稍后使用foo ,会怎么样?如果一个变量不应该是空的,但是是……你的应用程序需要得到一个NPE,这样你作为开发人员就可以真正地修复潜在的原因。
另请参见en.wikipedia.org/wiki/yoda_条件
在Java 8中,新的EDCOX1×6类可以很好地解决一些问题。至少可以说,它提高了代码的可读性,在公共API的情况下,可以使客户端开发人员更清楚地了解API的契约。
他们是这样工作的:
给定类型(Fruit 的可选对象)被创建为方法的返回类型。它可以是空的,也可以包含一个Fruit 对象:
1 2 3 4 5 6 7 8
public static Optional
< Fruit
> find
( String name, List
< Fruit
> fruits
) {
for ( Fruit fruit
: fruits
) {
if ( fruit.
getName ( ) .
equals ( name
) ) {
return Optional.
of ( fruit
) ;
}
}
return Optional.
empty ( ) ;
}
现在看看下面的代码,我们在其中搜索一个给定水果实例的Fruit (fruits )列表:
1 2 3 4 5
Optional
< Fruit
> found
= find
( "lemon" , fruits
) ;
if ( found.
isPresent ( ) ) {
Fruit fruit
= found.
get ( ) ;
String name
= fruit.
getName ( ) ;
}
可以使用map() 运算符对可选对象执行计算,或从中提取值。orElse() 允许您为缺少的值提供回退。
1 2 3
String nameOrNull
= find
( "lemon" , fruits
)
.
map ( f
-> f.
getName ( ) )
.
orElse ( "empty-name" ) ;
当然,检查空/空值仍然是必要的,但至少开发人员意识到该值可能是空的,忘记检查的风险是有限的。
在使用Optional 从头构建的API中,每当返回值可能为空时,并且仅当普通对象不能为null 时返回它(约定),客户端代码可能会放弃对简单对象返回值的空检查…
当然,Optional 也可以用作方法参数,在某些情况下,可能比5或10个重载方法更好地指示可选参数。
Optional 提供了其他方便的方法,例如允许使用默认值的orElse 和使用lambda表达式的ifPresent 。
我请你阅读这篇文章(我写这个答案的主要来源),其中NullPointerException (和一般的空指针)有问题,以及EDCOX1(13)所带来的(部分)解决方案得到了很好的解释:Java可选对象。
谷歌的番石榴有一个可选的Java 6 +。
必须强调的是,仅在ifpresent()中使用可选选项不会在正常的空检查之上增加太多的值。它的核心价值在于,它是一个可以在map/flapmap的函数链中使用的monad,其结果类似于其他地方提到的groovy中的elvis操作符。但是,即使没有这种用法,我发现orelse/orelsthrow语法也非常有用。
这个博客在可选的winterbe.com/posts/2015/03/15/avoint-null-checks-in-java上有一个很好的条目。
我真的不明白为什么人们对这个样板代码dzone.com/articles/java-8-elvis-operator如此满意
为什么人们倾向于用if(optional.isPresent()){ optional.get(); } 代替optional.ifPresent(o -> { ...})
所以,除了API契约提示之外,它实际上只是为了迎合那些喜欢无限链接方法的功能性程序员。
根据您要检查的对象的类型,您可能能够使用apache commons中的一些类,例如:apache commons lang和apache commons集合
例子:
1 2 3 4 5
String foo
;
...
if ( StringUtils.
isBlank ( foo
) ) {
///do something
}
或者(取决于您需要检查的内容):
1 2 3 4 5
String foo
;
...
if ( StringUtils.
isEmpty ( foo
) ) {
///do something
}
StringUtils类只是众多类中的一个;在公共空间中有相当多的好类执行空安全操作。
下面是一个例子,说明如何在使用Apache库时使用Java中的空值(CONSIX-LAN-2.4.jar)
1 2 3 4
public DOCUMENT read
( String xml, ValidationEventHandler validationEventHandler
) {
Validate.
notNull ( validationEventHandler,
"ValidationHandler not Injected" ) ;
return read
( new StringReader ( xml
) ,
true , validationEventHandler
) ;
}
如果您使用的是Spring,那么Spring在其包中也具有相同的功能,请参见库(Spring-2.4.6.jar)
关于如何从Spring使用这个静态classf的示例(org.springframework.util.assert)
1
Assert .notNull ( validationEventHandler,"ValidationHandler not Injected" ) ;
另外,您还可以使用ApacheCommons中更通用的版本,在方法开始时非常有用,可以检查我找到的参数。validate.not null(object,"object不得为空");commons.apache.org/lang/apidocs/org/apache/commons/lang/&hellip;
@MonoJohnny是否验证使用断言语句?.I要求这样做,因为assert可以在JVM上激活/停用,建议不要在生产中使用。
不要这样认为-我相信如果验证失败,它只会抛出一个runtimeexception
如果您认为一个对象不应该为空(或者它是一个bug),请使用断言。
如果方法不接受空参数,请在javadoc中声明它并使用断言。
你必须检查物体!=仅当要处理对象可能为空的情况时才为空…
建议在Java7中添加新注释,以帮助处理空/不空参数:http://tech.puredanger.com/java7/jsr308
我喜欢"快速失败"代码。问问你自己-在参数为空的情况下,你在做什么有用的事情吗?如果你对你的代码在这种情况下应该做什么没有一个明确的答案…也就是说,它一开始不应该是空的,然后忽略它并允许抛出NullPointerException。调用代码对NPE的意义与对IllegalArgumentException的意义相同,但是如果抛出了NPE,开发人员就更容易调试和理解出什么问题,而不是您的代码试图执行一些其他意外的应急逻辑,这最终导致应用程序失败。
最好使用断言,即contract.notnull(abc,"abc必须是非空的,它在xyz期间未能加载吗?")这是一种比执行if(abc)更紧凑的方法。=空)引发新的RuntimeException…
google collections框架提供了一种良好而优雅的方法来实现空检查。
在库类中有一个这样的方法:
用法是(与import static 一起使用):
1 2 3 4 5 6 7 8 9 10
...
void foo( int a, Person p) {
if ( checkNotNull( p) .getAge ( ) > a) {
...
}
else {
...
}
}
...
或者在您的示例中:
1
checkNotNull( someobject) .doCalc ( ) ;
嗯,有什么区别?p.getage()将抛出相同的NPE,开销更低,堆栈跟踪更清晰。我错过了什么?
最好在示例中抛出一个IllegalArgumentException("e==null"),因为它清楚地表明它是一个程序员想要的异常(以及足够的信息,使维护人员能够识别问题)。应该为JVM保留nullpointerExceptions,因为它清楚地表明这是无意的(通常发生在难以识别的地方)。
这是谷歌瓜娃的一部分。
对我来说,闻起来有点过于工程化的味道。只需让JVM抛出一个NPE,不要用这些垃圾来扰乱代码。
我喜欢它,打开大多数方法和构造函数,对参数进行显式检查;如果有错误,方法总是在前几行失败,我知道有问题的引用,但没有找到像getThing().getItsThing().getOtherThing().wowEncapsulationIsBroken().setLol("hi"); 这样的引用。
Java 1.7添加了EDCOX1和5的方法(除此之外)。这是一个很常见的成语,我很惊讶花了这么长时间!
只是为了兴趣…如果checknotnull(bla).foo()和bla.foo()都可以在null的情况下抛出一个NPE,那么它们之间的区别是什么?零收益,对吗?
有时,您有一些方法对定义对称操作的参数进行操作:
如果你知道b不能为空,你可以交换它。它对平等者最有用:与foo.equals("bar"); 相比,"bar".equals(foo); 更好。
但是,您必须假设equals (可以是任何方法)将正确处理空值。实际上,所有这些都是将责任传递给其他人(或其他方法)。
@优势基本上是肯定的,但不管怎样,equals 或任何方法都必须检查null 。或者明确声明它没有。
您可以考虑空对象是bug的情况,而不是空对象模式(它有其用途)。
当抛出异常时,检查堆栈跟踪并解决bug。
问题是,由于nullPointerException不指示哪个变量是空的,所以您通常会释放上下文,并且您可能在该行上有几个"."操作。使用"if(foo==null)throw new runtimeexception"("foo==null")可以显式地说明出了什么问题,为那些必须修复它的人提供了更多的堆栈跟踪值。
对于安徒生,我希望Java的异常系统能够包含正在被处理的变量的名称,这样Null PoExtExcices不仅表示异常发生的行,而且还表示变量名。这在未加密的软件中应该可以正常工作。
我还没有设法让它工作,但这正是为了解决这个问题。
有什么问题?只需在适当的级别捕获NPE,其中有足够的上下文可用,转储上下文信息并重新引发异常…它很容易用Java。
我有一位教授鼓吹反对方法调用链接。他的理论是,您应该警惕超过2个方法的调用链。我不知道这是否是一个硬规则,但它确实消除了NPE堆栈跟踪的大部分问题。
Java 7有一个新的EDCOX1 0实用工具类,其中有一个EDCOX1×1的方法。如果参数为空,那么只需抛出一个NullPointerException ,但它会清除一点代码。例子:
1 2
Objects.requireNonNull ( someObject) ;
someObject.doCalc ( ) ;
该方法对于在构造函数中的赋值之前进行检查最有用,每次使用它都可以保存三行代码:
1 2 3 4 5 6
Parent
( Child child
) {
if ( child
== null ) {
throw new NullPointerException ( "child" ) ;
}
this .
child = child
;
}
变成
1 2 3
Parent( Child child) {
this .child = Objects.requireNonNull ( child,"child" ) ;
}
实际上,您的示例构成了代码膨胀:第一行是多余的,因为NPE将在第二行中抛出。;-)
真的。更好的例子是,如果第二行是doCalc(someObject) 。
视情况而定。如果您是docalc()的作者,我建议将检查放入该方法的主体(如果可能的话)。然后您很可能会调用someObject.someMethod(),这里再次不需要检查空值。-)
好吧,如果你不是doCalc() 的作者,并且在给定的空值时它不会立即抛出npe,那么你需要检查空值并自己抛出npe。这就是Objects.requireNonNull() 的目的。
不仅仅是代码膨胀。最好是提前检查,而不是中途检查,因为这种方法会产生副作用或使用时间/空间。
我可能不会抛出一个NPE,当我在检查空值的参数时,我通常会抛出一个IllegalArgumentException,并且在最上面做这不是一件坏事,如果您假定这一点。child=child将"捕获"它,您的代码中没有显式化,重构可能会使捕获空值的来源变得非常困难。任何时候,代码膨胀都比魔法强得多。
Objects.requireNonNull(object, string) 版本(您可以将错误消息作为第二个参数传递)比单个arg版本(后者可能只是为了完整性而添加)更有用,特别是当您使用它检查多个参数值时。假设您必须检查3个参数是否为空,这将使代码从3个if/then抛出块(用空行分隔)减少到3个不需要空行的单行。
空不是"问题"。它是完整建模工具集不可或缺的一部分。软件旨在模拟世界的复杂性,而零则承担其负担。NULL在Java等中表示"没有数据"或"未知"。因此,为这些目的使用nulls是合适的。我不喜欢"空对象"模式;我认为它会提高"谁来保护"监护人的问题。< BR>如果你问我女朋友叫什么名字,我会告诉你我没有女朋友。在Java语言中,我将返回NULL。另一种方法是抛出有意义的异常,指出一些无法(或不希望)在那里解决的问题,并将其委托给堆栈中更高的位置,以重试或向用户报告数据访问错误。好的。
对于"未知问题",给出"未知答案"。(如果从业务角度来看这是正确的,请确保为空)在方法中使用前检查一次参数是否为空将使多个调用方在调用前不再检查这些参数。好的。
1 2 3 4 5 6
public Photo getPhotoOfThePerson( Person person) {
if ( person == null )
return null ;
// Grabbing some resources or intensive calculation
// using person object anyhow.
}
以前的逻辑流程是正常的,从我的照片库中找不到不存在的女朋友的照片。好的。
1
getPhotoOfThePerson( me.getGirlfriend ( ) )
它适合新的Java API(展望未来)好的。
1
getPhotoByName( me.getGirlfriend ( ) ? .getName ( ) )
虽然对于某些人来说,找不到存储在数据库中的照片是相当"正常的业务流程",但对于其他一些情况,我使用下面这样的对好的。
1 2
public static MyEnum parseMyEnum
( String value
) ; // throws IllegalArgumentException
public static MyEnum parseMyEnumOrNull
( String value
) ;
并且不要讨厌输入 + + (在Eclipse中生成javadoc)并为公共API编写另外三个单词。除了那些不阅读文档的人之外,这对所有人都足够了。好的。
1 2 3
/**
* @return photo or null
*/
或好的。
1 2 3
/**
* @return photo, never null
*/
这是相当理论性的情况,在大多数情况下,您应该更喜欢Java NULL安全API(如果它将在10年内发布),但是EDCOX1(1)是EDCOX1×2的子类。因此,它是Throwable 的一种形式,表示一个合理的应用程序可能想要捕获(javadoc)的条件!为了使用异常的第一个最大优点,并从"正则"代码中分离错误处理代码(根据Java的创建者),对于我来说,捕获EDCOX1×1是合适的。好的。
1 2 3 4 5 6 7
public Photo getGirlfriendPhoto
( ) {
try {
return appContext.
getPhotoDataSource ( ) .
getPhotoByName ( me.
getGirlfriend ( ) .
getName ( ) ) ;
} catch ( NullPointerException e
) {
return null ;
}
}
可能会出现以下问题:好的。
问:如果getPhotoDataSource() 返回空值怎么办?A.这取决于业务逻辑。如果我找不到相册,我就不给你看照片。如果AppContext未初始化怎么办?这个方法的业务逻辑支持了这一点。如果相同的逻辑更严格,那么抛出一个异常,它是业务逻辑的一部分,应该使用对空值的显式检查(情况3)。新的Java NULL安全API更适合于在有程序员错误的情况下有选择地指定隐含的含义以及不意味着要初始化为失败的东西。好的。
可以执行冗余代码并获取不必要的资源。a.如果getPhotoByName() 尝试打开数据库连接,创建PreparedStatement 并最终使用人名作为SQL参数,则可能发生这种情况。一个未知问题的方法给出了一个未知答案(案例1)。在获取资源之前,该方法应检查参数并在需要时返回"未知"结果。好的。
问:此方法因尝试关闭打开而受到性能惩罚。a.软件首先应易于理解和修改。只有在这之后,人们才能考虑性能,并且只有在需要的时候才考虑性能!在需要的地方!(来源),还有许多其他的。好的。
另外,这种方法的合理使用将与在某些地方合理使用"常规"代码原则中的单独错误处理代码一样。考虑下一个例子:好的。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31
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是EDCOX1×2的子类。在Java历史上,我们有一个先例,当EDCOX1×3是EDCOX1×4时,不是因为它实际上是一个应用程序错误,而是因为它不打算被捕获。多少NPE适合作为一个Error 比ThreadDeath !但事实并非如此。好的。
仅当业务逻辑暗示"无数据"时才检查"无数据"。好的。
1 2 3 4 5 6 7 8 9 10 11 12 13 14
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
) ;
}
}
和好的。
1 2 3 4 5 6 7 8 9 10
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。好的。
好啊。
仅仅因为它的意思是"没有数据",并不意味着它应该被使用。null 的语言实现很糟糕。
抓到NullPointerException ,返回null ,调试起来很糟糕。不管怎样,你最终还是会得到NPE,很难弄清楚什么是最初的空值。
您的示例的问题是,getphotooftpherson实际上不需要决定是否返回一张照片,或者如何处理一个空的人——直接读取方法名表示它返回一个人的照片。它不应该返回空值,因为它不(也不应该)知道调用方将如何处理空照片。另一方面,调用者应该知道当这个人为空时该怎么做。Java没有空的扩展,这将消除这个问题,因为它会知道如何处理零使用和使用它。
如果我有这样的名声,我会投反对票。它不仅是空的,而且是类型系统中的一个漏洞。将树分配给列表是一个类型错误,因为树不是类型列表的值;按照相同的逻辑,分配空值应该是一个类型错误,因为空值不是类型对象的值,或者与此相关的任何有用类型的值。即使是发明了"零"的人也认为这是"十亿美元的错误"。"可能是t型或无型值的值"的概念是它自己的类型,应该这样表示(例如maybe或optional)。
至于"把树分配给一个列表",你不是在说Java,是吗?因为Java是"编译时严格类型"语言(它的内存模型不同于C++,C语言)。Java编译器不会给您分配树到列表的机会。
从"maybeor optional开始,您仍然需要编写类似于if (maybeNull.hasValue()) {...} 的代码,那么if (maybeNull != null)) {...} 有什么区别?
至于"Java没有空传播,这将消除问题",通常传播是关于"例外情况"。但在大多数情况下,数据可用性或不可用性是常见的业务流。如果数据不可用(由业务决定)中存在异常情况,您应该显式地抛出合理的异常,并给出合理的消息。传播为空(是否包含行号?……对我来说真的很可怕。
因为"捕捉nullpointerException并返回null对于调试来说是可怕的。不管怎样,你最终还是会得到NPE,很难弄清楚什么是最初的空值"。我完全同意!在这种情况下,如果业务逻辑隐含了数据,或者使用新Java的NULL安全操作符,您应该编写十几个"IF"语句或抛出NPE。但有些情况下,我不在乎具体的步骤会给我零。例如,在屏幕上显示预期数据可能丢失之前,为用户计算一些值。
@Mykhayoladamovych:Maybe 或Optional 的好处不是在你的T 可能无效的情况下,但在这种情况下,它不应该是无效的。如果您的类型显式地表示"此值可能为空--小心使用",并且您始终使用并返回这样的类型,那么每当您在代码中看到一个普通的旧T ,您就可以假定它从不为空。(当然,如果编译器可以强制执行,这将更有用。)
同意。但您不会减少代码中的"空虚检查"(我们正在讨论的主题),但您会更安全,但它是有代价的。
"即使是发明了零的人也认为这是他的"十亿美元的错误"——在发明任何东西之前,零就已经存在了。人们可以发明一套规则来以特定的方式处理它,但这将是"另一个功能完整的系统"。我想知道在这种非故障快速系统中处理细微的错误要花多少钱。也许由于新的趋势,我们很接近于比较这些系统。
"一个值可能是T值或无值"的概念是它自己的类型,应该表示为这样(例如,可能是或可选)-你可能认为Java中的所有引用都是可选的,你可以通过使用未使用的最终关键字或引入强制来切换到定义初始化的。那有什么区别呢?…无论如何,我更喜欢当前的Java默认值,因为我们周围的东西是相当随意的。"烧饭前先把水壶装满水"和"离开前车里有燃料"等都是可选的。
当前正在读取有关"算术表达式中的空值"的Oracle数据库引用。它声明:"如果算术表达式中的任何列值为空,则结果为空。"例如,如果您试图执行除数为零,则会得到一个错误。但是,如果你把一个数字除以零,结果是一个零或者未知的。'但是,不要介意,这完全不符合逻辑,在甲骨文的某个地方应该有一个愚蠢的家伙,他必须被解雇直到这些天…
@Mykhayloadamovych:如果数组项的有意义的值将以任意顺序已知,并且在将有意义的值分配给所有数组项之前必须读取某些数组项,则通常不可能静态地排除在确定有意义的值之前读取项的可能性。如果霍尔不喜欢空值,我想知道他更喜欢什么样的默认值或行为?从数组中读取空值时的补漏白有时可能比等到它被取消引用时再补漏白要好,但会使复制变得不可能…
…通过迭代复制其中的项而部分填充的数组。我能看到的唯一其他选择是,尝试取消对从尚未写入的数组槽中读取的指针的引用将访问一些默认对象(在大多数情况下,这看起来很愚蠢),或者具有C样式的"未定义行为"。我认为捕获空指针比我能看到的任何其他方法都好。
@Mykhayoladamovych看到了它的速度有多快,这是其他人以前做过的。如果Java根本没有空值,那么就没有办法将NULL存储到字段/变量中;如果要处理它是可选的,则必须有一个可选的。你会问-那会得到什么?你不需要那么多支票吗?不,您不会这样做,因为编译器将确保每个非可选项都从不为空,并且超过一半的检查是针对从不应为空且从不为空的对象的,或者如果是这样,则这是一个bug,编译器可能会捕获该bug,或者由接口公开该bug。
@在编译器确保可选的(或曾经的)不为空之后,我不跟随Entonio,然后呢?你是说这个人的申请是为了永远不会遇到没有女朋友的情况吗?应用程序如何处理这些场景,是否有多种类型的人,也许我们创建了两种类型的人,Lonelyperson和ChackledPerson?
"Endoio,Java有可选的,这是非最终引用。Java编译器确保每个最终版本都被初始化。如果您的资历足够高,不编写样板代码,那么您将拥有相同数量的检查,因为"空性"是一个业务概念。Java可选的没有EDCX1→0和EDOCX1→1模板。
最终,完全解决这个问题的唯一方法是使用不同的编程语言:
在objective-c中,您可以在nil 上调用方法,而绝对不会发生任何事情。这使得大多数空检查不必要,但它会使错误更难诊断。
在Java派生语言Nice中,有两种类型的版本:潜在的空版本和非空版本。只能对非空类型调用方法。通过显式检查空值,可以将可能为空的类型转换为非空的类型。这使得更容易知道哪里需要空检查,哪里不需要空检查。
哇…最正确的答案会被否决,正义在哪里?在JavaNULL中始终是一个有效的值。这是所有事物都是一个对象的必然结果——空是所有事物(当然,我们这里忽略了原语,但你明白了)。就我个人而言,我赞成NICE采用的方法,尽管我们可以这样做,以便可以对可为空的类型调用方法,并将NPE提升为已检查的异常。这必须通过编译器开关完成,因为它会破坏所有现有的代码:(
我不熟悉nice,但是Kotlin实现了相同的思想,在语言的类型系统中构建了可为空和不为空的类型。比选项或空对象模式简洁得多。
Java中常见的"问题"。
首先,我的想法是:
我认为当传递空值时"吃"一些东西是不好的,因为空值不是有效值。如果您不退出带有某种错误的方法,那么这意味着您的方法中没有任何错误,这不是真的。在这种情况下,您可能会返回空值,在接收方法中,您再次检查空值,它永远不会结束,最后会得到"if!"= NULL"等。
因此,imho,空值必须是一个严重错误,它会阻止进一步执行(也就是说,空值不是有效值)。
我解决这个问题的方法是:
首先,我遵循这一惯例:
所有公共方法/api始终检查其参数是否为空
所有私有方法都不检查空值,因为它们是受控制的方法(如果上面没有处理空值指针异常,就让它与空值指针异常一起消失)
唯一不检查空值的其他方法是实用工具方法。它们是公开的,但是如果您出于某种原因调用它们,您就知道您传递了哪些参数。这就像是在不加水的情况下把水壶里的水烧开…
最后,在代码中,公共方法的第一行如下:
1
ValidationUtils.getNullValidator ( ) .addParam ( plans,"plans" ) .addParam ( persons,"persons" ) .validate ( ) ;
请注意,addParam()返回self,以便添加更多要检查的参数。
方法validate() 会在参数为空时抛出检查过的ValidationException (检查或不检查更多是设计/品味问题,但我的ValidationException 是检查过的)。
1
void validate( ) throws ValidationException;
例如,如果"plans"为空,则消息将包含以下文本:
"参数[计划]遇到非法参数值空值"
正如您所看到的,用户消息需要addParam()方法(字符串)中的第二个值,因为您不容易检测到传入的变量名,即使使用反射(无论如何都不是本文的主题…)。
是的,我们知道在这一行之外,我们将不再遇到空值,所以我们只是安全地调用这些对象上的方法。
通过这种方式,代码是干净的,易于维护和可读的。
当然。仅仅抛出错误和崩溃的应用程序具有更高的质量,因为毫无疑问,当它们不工作时。吞咽错误的应用程序最好能优雅地降级,但通常不会以难以注意和无法修复的方式工作。当发现问题时,它们就很难调试。
除了使用assert 外,还可以使用以下内容:
1 2 3
if ( someobject == null ) {
// Handle null here then move on.
}
这比:
1 2 3 4 5 6 7 8
if ( someobject != null ) {
.....
.....
.....
}
嗯,为什么?请不要感到任何防御,我只想了解更多关于Java:
@作为一般规则,我更喜欢if语句中的表达式是一个更"积极"的语句,而不是一个"消极"的语句。因此,如果我看到if (!something) { x(); } else { y(); } ,我会倾向于将其重构为if (something) { y(); } else { x(); } (尽管有人可能认为!= null 是更积极的选择…)。但更重要的是,代码的重要部分并没有包装在{} 中,而且对于大多数方法,您的缩进量要小一级。我不知道这是否是FastcodeJava的推理,但那是我的。
这也是我想做的……在我的视野中保持代码的清洁。
提出这个问题表明您可能对错误处理策略感兴趣。您团队的架构师应该决定如何处理错误。有几种方法可以做到这一点:
允许异常在"主循环"或其他一些管理例程中进行传播。
当然,也有一个面向方面的编程——它们有很好的方法将if( o == null ) handleNull() 插入到字节码中。
不要使用空值。不要让它。
在我的类中,大多数字段和局部变量都有非空的默认值,并且我在代码中的任何地方都添加了契约语句(总是使用断言),以确保强制执行该语句(因为它比让它成为NPE,然后必须解析行号等更简洁、更具表现力)。
一旦我采用了这种做法,我注意到问题似乎自己解决了。在开发过程中,您会很早就意外地发现一些事情,并意识到您有一个弱点。更重要的是……它有助于封装不同模块的关注点,不同的模块可以互相"信任",并且不再将代码与if = null else 构造混淆!
这是一种防御性编程,从长远来看会产生更干净的代码。始终清理数据,例如通过强制执行严格的标准来解决问题。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
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,你必须以某种方式弄清楚。
为什么会投反对票?根据我的经验,这远远优于其他方法,我想知道为什么不
我同意,这种方法可以防止与空值相关的问题,而不是通过在任何地方指定代码空值检查来解决问题。
这种方法的问题在于,如果从未设置名称,它的值为"",其行为类似于设置值。现在假设我需要检查名称是否从未设置(未知),我必须对特殊值""进行字符串比较。
史蒂夫,说得对。我经常做的是将该值作为常量,例如public static final string unset="uuunset"…private string field=取消设置…然后private boolean isset()返回unset.equals(field);
imho,这是一个空对象模式的实现,带有可选(contract)的您自己实现。它在持久性类上的行为如何?我认为这种情况不适用。
@库拉皮卡-同意。我认为"从不使用NULL"在Java中并不完全实用。但99%的时间,是的,而且是应该被鼓励的,在我看来,我喜欢用Kotlin这样的语言来解决这个问题。
guava是google非常有用的核心库,它有一个很好的有用的API来避免空值。我发现使用davoidingnull解释非常有用。
如wiki所述:
Optional 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."
用途:
1 2 3
Optional< Integer> possible = Optional.of ( 5 ) ;
possible.isPresent ( ) ; // returns true
possible.get ( ) ; // returns 5
@科迪古德纳对,科迪。我从链接中提供了一个相关的引用以提供更多的上下文。
对于每个Java开发人员来说,这是一个非常常见的问题。因此,Java&Nbsp 8中有官方的支持来解决这些问题而没有混乱的代码。
Java 8已经介绍了EDCOX1 0。它是一个可能包含或不包含非空值的容器。Java 8提供了一种更安全的方式来处理在某些情况下其值可能为空的对象。它的灵感来自哈斯克尔和斯卡拉的思想。
简而言之,可选类包括显式处理值存在或不存在的情况的方法。但是,与空引用相比的优势在于,可选的类强制您在值不存在时考虑这种情况。因此,可以防止意外的空指针异常。
在上面的示例中,我们有一个家庭服务工厂,它将一个手柄返回到家庭中可用的多个设备。但这些服务可能可用,也可能不可用;这意味着它可能导致nullpointerException。在使用任何服务之前,我们不要添加一个空的if 条件,而是将其包装到可选的中。
包装到选项
让我们考虑一种从工厂获取服务引用的方法。不要返回服务引用,而是用可选的方法包装它。它让API用户知道返回的服务可能可用,也可能不可用/不起作用,以防御的方式使用
1 2 3 4 5
public Optional< Service> getRefrigertorControl( ) {
Service s = new RefrigeratorService( ) ;
//...
return Optional.ofNullable ( s) ;
}
如您所见,Optional.ofNullable() 提供了一种简单的方法来包装引用。还有另一种方法可以获得可选的引用,即Optional.empty() 和Optional.of() 。一个用于返回空对象而不是重新调整空对象,另一个用于分别包装不可为空的对象。
那么,避免空检查究竟有什么帮助呢?
包装引用对象之后,可选选项提供了许多有用的方法来调用不带NPE的包装引用上的方法。
1 2
Optional ref = homeServices.getRefrigertorControl ( ) ;
ref.ifPresent ( HomeServices:: switchItOn) ;
可选。如果给定使用者是非空值,则使用引用调用该使用者。否则,它什么也不做。
1 2
@FunctionalInterface
public interface Consumer< T>
表示接受单个输入参数且不返回结果的操作。与大多数其他功能接口不同,消费者需要通过副作用进行操作。它很干净,很容易理解。在上面的代码示例中,如果可选保持引用为非空,则调用HomeService.switchOn(Service) 。
我们经常使用三元运算符检查空条件,并返回可选值或默认值。可选提供了另一种处理相同条件而不检查空值的方法。可选。如果可选值为空,则orelse(defaultobj)返回defaultobj。让我们在示例代码中使用它:
1 2 3 4
public static Optional< HomeServices> get( ) {
service = Optional.of ( service.orElse ( new HomeServices( ) ) ) ;
return service;
}
现在homeservices.get()做了同样的事情,但以更好的方式。它检查服务是否已经初始化了not。如果是,则返回相同的服务或创建新的服务。可选的.orelse(t)有助于返回默认值。
最后,这里是我们的NPE以及空的免检查代码:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29
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以及空的免检查代码…真的吗?.
我喜欢Nat Pryce的文章。以下是链接:
使用多态调度避免空值
使用"告诉,不要问"样式避免空值
在文章中,也有一个链接到一个Java类型的Git存储库,我觉得它很有趣,但我不认为它可以减少正在检查代码膨胀。在网上做了一些调查之后,我想!=空代码膨胀主要通过精心设计来减少。
Michael Feathers写了一篇简短有趣的文章,介绍了你提到的方法:manuelp.newslur.com/site/424
我试过用电刀,但对我来说并不总是最好的方法。有时"不采取行动"是不恰当的。
NullPointerException 是一个运行时异常,这意味着它是开发人员的错误,有了足够的经验,它可以准确地告诉您错误在哪里。
现在回答:
尽量将所有属性及其访问器设置为私有的,或者避免将它们暴露给客户机。当然,您可以在构造函数中使用参数值,但是通过缩小范围,您不会让客户机类传递无效的值。如果需要修改这些值,则可以始终创建一个新的object 。只检查一次构造函数中的值,在其余的方法中,几乎可以确定这些值不是空的。
当然,经验是更好地理解和应用这个建议的方法。
字节!
对于Java 8或更新器来说,最好的替代方法是使用EDCOX1×9类。
1 2
Optional stringToUse
= Optional.
of ( "optional is there" ) ;
stringToUse.
ifPresent ( System .
out :: println
) ;
这对于可能为空值的长链特别方便。例子:
1 2 3 4
Optional< Integer> i = Optional.ofNullable ( wsObject.getFoo ( ) )
.map ( f -> f.getBar ( ) )
.map ( b -> b.getBaz ( ) )
.map ( b -> b.getInt ( ) ) ;
关于如何在null上引发异常的示例:
1 2
Optional optionalCarNull
= Optional.
ofNullable ( someNull
) ;
optionalCarNull.
orElseThrow ( IllegalStateException :: new ) ;
Java 7介绍了EDCOX1×10的方法,它可以方便地检查一些非空的情况。例子:
1
String lowerVal
= Objects.
requireNonNull ( someVar,
"input cannot be null or empty" ) .
toLowerCase ( ) ;
我可以更一般地回答吗?
当方法以我们不期望的方式获得参数时,我们通常会遇到这个问题(错误的方法调用是程序员的错误)。例如:您期望得到一个对象,而不是得到一个空值。您希望得到一个至少包含一个字符的字符串,而不是一个空字符串…
因此,两者之间没有区别:
1 2
if ( object == null ) {
//you called my method badly!
}
或
1 2 3
if ( str.length ( ) == 0 ) {
//you called my method badly again!
}
他们都想确保我们在执行任何其他函数之前收到了有效的参数。
正如其他一些答案中提到的,为了避免上述问题,您可以按照契约模式进行设计。请参见http://en.wikipedia.org/wiki/design_by_contract。
为了在爪哇中实现这种模式,可以使用JavaX.AnNOCTION.NOTULL之类的核心Java注释,或者使用更复杂的库,如Hibernate验证器。
只是一个例子:
现在,您可以安全地开发方法的核心功能,而无需检查输入参数,它们可以保护您的方法不受意外参数的影响。
您可以更进一步,确保只能在应用程序中创建有效的POJO。(来自Hibernate验证程序站点的示例)
1 2 3 4 5 6 7 8 9 10 11 12 13 14
public class Car
{
@NotNull
private String manufacturer
;
@NotNull
@Size
( min
= 2 , max
= 14 )
private String licensePlate
;
@Min
( 2 )
private int seatCount
;
// ...
}
根据定义,EDCOX1×4是不是"核心Java"。
我高度忽视了建议在任何情况下使用空对象的答案。这种模式可能会破坏合同,并将问题埋得越来越深,而不是解决问题,更不用说不适当的使用将创建另一堆需要未来维护的样板代码。
实际上,如果从方法返回的内容可以为空,并且调用代码必须对此作出决定,那么应该有一个确保状态的早期调用。
还请记住,如果不小心使用空对象模式,那么它会占用内存。为此,nullObject的实例应该在所有者之间共享,而不是每个所有者的唯一实例。
我也不建议使用这种模式,在这种模式中,类型应该是一种原始类型的表示,如数学实体,而不是标量:向量、矩阵、复数和POD(普通的旧数据)对象,这些对象意在以Java内置类型的形式保存状态。在后一种情况下,您最终将使用任意结果调用getter方法。例如,nullPerson.getName()方法应该返回什么?
为了避免产生荒谬的结果,有必要考虑这些案例。
"hasBackground()"的解决方案有一个缺点——它不是线程安全的。如果需要调用两个方法而不是一个方法,则需要在多线程环境中同步整个序列。
@pkanlow您做了一个人为的例子,只是为了指出这个解决方案有一个缺点。如果代码不打算在多线程应用程序中运行,那么就没有缺点。我可以向您输入90%的代码,这些代码不是线程安全的。我们不是在这里讨论代码的这个方面,而是在讨论设计模式。多线程本身就是一个主题。
当然,在单线程应用程序中,这不是问题。我发表这一评论是因为有时这是个问题。
@pkalinow如果仔细研究这个主题,你会发现空对象设计模式不能解决多线程问题。所以这无关紧要。老实说,我已经找到了适合这种模式的地方,所以我最初的答案实际上有点错误。
从不将变量初始化为空。
如果(1)不可能,请将所有集合和数组初始化为空集合/数组。
用你自己的代码来做这件事,你可以避免!=空校验。
大多数时候,空检查似乎保护集合或数组上的循环,所以只要将它们初始化为空,就不需要任何空检查。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
// 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
}
}
这其中有一个很小的开销,但对于更干净的代码和更少的nullpointerException来说是值得的。
stackoverflow.com/questions/1386275/&hellip;
+我同意这一点。不应返回半初始化的对象。JAXB相关的代码和bean代码不适用于此。这是不好的做法。所有集合都应该初始化,并且所有对象都应该存在,并且(理想情况下)没有空引用。考虑一个包含集合的对象。检查对象是否不为空、集合是否不为空以及集合是否不包含空对象是不合理和愚蠢的。
这是大多数开发人员最常见的错误。
我们有很多方法来处理这个问题。
方法1:
1
org.apache .commons .lang .Validate //using apache framework
NotNull(对象对象、字符串消息)
方法2:
1 2
if ( someObject!= null ) { // simply checking against null
}
方法3:
1
@isNull @Nullable // using annotation based validation
方法4:
1 2 3 4 5 6 7 8
// 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
;
}
广告语4。它不是很有用-当您检查指针是否为空时,您可能希望对其调用一个方法。对空值调用方法会提供相同的行为-NullPointerException。
1 2 3 4 5 6
public static < T> T ifNull( T toCheck, T ifNull) {
if ( toCheck == null ) {
return ifNull;
}
return toCheck;
}
这个方法有什么问题,我认为@tltester只是想给出一个默认值,如果它为空,这是有意义的。
在ApacheCommonsLang中有这样一种方法:ObjectUtils.defaultIfNull() 。还有一个更一般的方法:ObjectUtils.firstNonNull() ,它可以用来实施一种降级策略:firstNonNull(bestChoice, secondBest, thirdBest, fallBack); 。
Java 8在JavaUTIL包中引入了一个可选的新类。
Java 8的优点可选:
1.)不需要空检查。2.)运行时不再出现空指针异常。3)我们可以开发干净整洁的原料药。
可选-可能包含或不包含非空值的容器对象。如果存在值,ispresent()将返回true,get()将返回该值。
有关更多详细信息,请参阅以下Oracle文档:https://docs.oracle.com/javase/8/docs/api/java/util/optional.html
您可以使用findbugs。它们还有一个Eclipse插件,可以帮助您找到重复的空检查(除其他外),但请记住,有时您应该选择防御性编程。也有JAVA合同,这可能是有帮助的。
我遵循以下准则以避免空检查。
尽可能避免成员变量的延迟初始化。初始化声明本身中的变量。这将处理NullPointerExceptions。
在周期的早期决定成员变量的可变性。有效使用final 关键字等语言结构。
如果您知道方法的扩充不会被更改,那么将它们声明为final 。
尽可能限制数据突变。一些变量可以在构造函数中创建,并且永远不能更改。删除公共setter方法,除非它们确实是必需的。
例如,假设应用程序中的一个类(A.java 正在维护类似HashMap 的集合。不要在a.java中提供public getter方法,允许B.java 直接在Map 中添加元素。相反,在A.java 中提供一个API,它将一个元素添加到集合中。
1 2 3 4 5 6 7 8 9
// 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) 类的方法进行空指针检查。
例如,
1 2 3 4 5 6 7 8
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" ) ;
总而言之,避免陈述
1 2 3
if ( object != null ) {
....
}
自Java 7以来,您可以使用EDCOX1和25种方法:
objects.isNull(对象)
对象.非空(对象)
对象。RequireNonnull(对象)
objects.equals(对象1,对象2)
自Java 8以来,可以使用可选类(何时使用)
EDCOX1〔29〕Java 8
EDCOX1〔30〕Java 9
依赖方法契约(JSR 305)并使用查找错误。用注释@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
当需要在空值时指定默认值时,请使用apache commons lang
public static Object defaultIfNull(Object object, Object defaultValue)
另一个选择:
下面的简单函数有助于隐藏空检查(我不知道为什么,但我没有发现它是同一公共库的一部分):
1 2 3
public static < T> boolean isNull( T argument) {
return ( argument == null ) ;
}
你现在可以写了
1 2 3
if ( ! isNull( someobject) ) {
someobject.doCalc ( ) ;
}
这是一种更好的表达!= null 的方法。
如果你总是要否定返回值,那么简单地写一个函数isNotNull 不是更好吗?这不是更清楚地表明你的意图吗?
@TMN:你说得对,理想情况下,我想同时使用isNull 和isNotNull 方法。
我看不出这比"someobject"更简洁。="空"。
像findbugs这样的编译时和运行时dev工具将很难识别重复的空检查。此外,您还希望Java嵌入IsNULL函数,
无论在何处传递数组或向量,请将其初始化为空数组,而不是空数组。-这样就可以避免大量检查空值,而且一切都很好:)
1 2 3 4 5 6 7 8 9 10 11 12 13
public class NonNullThing
{
Vector vectorField
= new Vector ( ) ;
int [ ] arrayField
= new int [ 0 ] ;
public NonNullThing
( ) {
// etc
}
}
在这种情况下,我发现番石榴的前提条件非常有用。我不喜欢将空值保留为空指针异常,因为了解NPE的唯一方法是定位行号。生产版本和开发版本中的行号可以不同。
使用guava前提条件,我可以检查空参数并在一行中定义一个有意义的异常消息。
例如,
1
Preconditions.checkNotNull ( paramVal,"Method foo received null paramVal" ) ;
为了避免NullPointerException ,你可以避免很多,只需遵循其他大多数问题的答案,我只想增加一些在Java 9 中引入的方法来优雅地处理这个场景,并且展示一些旧的方法也可以使用,从而减少你的努力。
public static boolean isNull(Object obj)
如果提供的引用为空,则返回true,否则返回错误的。
自Java 1.8
public static boolean nonNull(Object obj)
如果提供的引用不为空,则返回true,否则返回错误的。
自Java 1.8
public static T requireNonNullElse?(T obj, T defaultObj)
如果第一个参数非空,则返回该参数,否则返回非空的第二个参数。
自Java 9
public static T requireNonNullElseGet?(T obj, Supplier extends T> supplier)
如果第一个参数非空,则返回该参数,否则返回supplier.get()的非空值。
自Java 9
public static T requireNonNull?(T obj, Supplier messageSupplier)
检查指定的对象引用是否不为空,否则将引发自定义的NullPointerException。
自Java 1.8
有关上述功能的更多详细信息,请参见此处。
你可以看到有很多的答案有很多上票,你的答案有什么特别之处?
我只是想增加一些在Java 9 中可用的功能。
然后在文章开头用粗体强调
当然,谢谢你的反馈!
您可以在方法调用之前使用拦截器。这就是面向方面编程所关注的。
假设m1(对象测试)是一个方法,m2是一个方法,我们在方法调用之前应用方面,M2(Object test2) 。如果是test2 != null ,那么打M1,否则再做一件事。它适用于所有要应用方面的方法。如果要为实例字段和构造函数应用方面,可以使用AspectJ。对于方法方面,弹簧也可以是最佳选择。
在Java 8中,如果本地变量/字段/方法参数/方法返回的类型不指定EDCOX1引用12,则可以使用EDCOX1类型11的方法(并且不检查EDOCX1引用12),或者如果它可以是EDCOX1,12,则键入EDCOX1×14。然后用方法Map 处理T -> ,方法flatMap 处理T -> Optional :
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61
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
; }
}
如果您使用的是java8或更高版本,请从java.util.Objects 购买isNull(yourObject) 。
例子:
1 2 3
String myObject
= null ;
Objects.
isNull ( myObject
) ; //will return true
这与myObject == null 没有太大的区别,在java8中针对Predicates 引入了lambda特性。
objects.isNull()在java8(而不是java7)之后可用。
@realhow感谢您的指出,我已经更新了答案,实际上Objects 是在java7中引入的,isNull 是在后面添加的。
您还可以使用checker框架(使用JDK7和更高版本)静态检查空值。这可能会解决很多问题,但需要运行一个额外的工具,目前只适用于OpenJDK Afaik。https://checkerframework.org网站/
Java 8现在有一个可选的类,它封装了对象,如果存在一个值,IsStand()将返回true,并且GET()将返回该值。
http://www.oracle.com/technetwork/articles/java/java8-optional-2175753.html
好的,我已经在技术上回答了一百万次,但是我必须这么说,因为这是一个与Java程序员的不间断的讨论。
对不起,我不同意以上几乎所有的说法。我们必须在Java中测试NULL的原因是,Java程序员不知道如何处理内存。
我之所以这么说是因为我在C++中有很长的编程经验,我们不这么做。换句话说,你不需要。请注意,在Java中,如果碰到一个悬空指针,就会得到一个正常的异常;在C++中,这个异常通常不会被捕获并终止程序。
不想这样做?然后遵循一些简单的规则ALC/C/C++。
不要那么容易地例示事物,认为每一个"新"都会给你带来很多麻烦,并遵循这些简单的规则。
一个类只能以3种方式访问内存->
它可以"拥有"类成员,并且他们将遵循以下规则:
所有"has"成员都在构造函数中创建为"new"。
您将在析构函数或等效close()中关闭/取消分配在Java中为同一类和在其他类中的函数。
这意味着您需要记住(就像Java一样),每个资源的所有者或家长,并尊重所有权。对象只被创建它的类删除。也>
有些成员将被"使用",但不是自己的或"拥有"。这是另一个类中的"own",并作为参数传递给构造函数。因为这些是由另一个类拥有的,所以我们永远不会删除或关闭它,只有父类可以。
类中的方法还可以实例化本地对象供内部使用,这些对象永远不会从类的一侧传递出去,或者它们应该是普通的"has"对象。
最后,为了使所有这些工作正常,您需要有一个规范的设计,其中类以层次结构的形式存在,并且不进行循环。
在这种设计下,按照上面的规则,层次结构设计中的子类无法访问被破坏的指针,因为这意味着父类在子类之前被破坏,而层次结构非循环设计不允许这样做。
最后,还要记住,当启动系统时,应该从层次结构的顶部到底部构建,并从底部到顶部销毁。任何地方都不会有空指针,或者有人违反了规则。
同意,尽管只是概念上的。是的,应该有个计划。但是,您需要能够制定计划并具有逻辑思考能力、开发和维护一致模式的程序员。还有一些雇主,他们的主要关注点是金钱和工资;—)。然后开始查看JPA规范,了解什么构成了一致的模式/计划,以及开发和/或记录它需要什么。
这个答案与C++相关,但不适用于像Java那样的垃圾收集语言。
我尊重地不同意。Java(或几乎任何其他语言)的良好内存处理将给您带来很多好处。从零指针减少时的更好稳定性、内存占用减少时的更好性能,到关系得到更好控制时的更好设计。我会建议,许多Java应用程序的草率内存管理会导致频繁的NPEs和内存泄漏。
我非常同意你所写的,并认为非常不幸的是Java没有区分封装所有权和不属于所有权的引用。那些认为"GC就是这样做"的人忽略了一个关键点:GC避免了不可变对象拥有所有者的需要,并且它也消除了对被删除对象的悬空引用的可能性,这使得它成为对其他任意对象(如C++中可能发生的)的引用。但是,如果多个对象将可变对象的状态封装为其自身的一部分,则很难编写正确的代码。
我最喜欢的思考方式是说引用可以封装身份、可变状态,两者都可以,或者两者都不能。此外,引用可以封装可变状态,这可能是因为它们标识了一个不可变的对象,也可能是因为它们标识的对象永远不会暴露于任何可能使其发生变化的对象中。太糟糕了,Java没有这种区别的概念,因为平等检查和克隆之类的东西如果自动处理的话,可以自动处理99%。
我相信你的建议只会解决Java中空指针问题的一小部分,并不能解决这里所描述的原始问题。
它可能比你想象的更重要。更多信息:ibm.com/developerworks/library/j-jtp06243:)
"[……]任何地方都不会有空指针,或者有人违反了规则。"违反规则的人正是问题的关键。或者你不使用一个库就写了100%的代码?
当然,你不能控制别人的代码。但是这些规则仍然适用,而且很有用。另一方面,它就像是说,如果需要访问任何库,rust(cmr.github.io/blog/2013/08/13/rust-by-concept)将永远不会工作。所以,这确实是一个问题,但是遵循指针所有权可以大大降低风险。
首先,我们不能真正删除所有空条件。我们可以使用@NotNull 和@Nullable 注释(如前所述)来减少它们。但这需要一些框架的支持。这是椭圆形可以帮助的地方。
基本思想是对象/参数/构造函数应始终满足前提条件。您可以有很多前提条件,例如Nullable 、NotNull 和oval,它们会注意在调用时对象应该处于一致状态。
我猜椭圆形内部使用AspectJ来验证前提条件。
1 2 3 4 5 6 7 8 9 10
@Guarded
public class BusinessObject
{
public BusinessObject
( @NotNull
String name
)
{
this .
name = name
;
}
...
}
例如,
1 2
// Throws a ConstraintsViolatedException because parameter name is null
BusinessObject bo = new BusinessObject( null ) ;
避免不必要的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 部分,因为您如何确定变量不能为空?好的。
对此没有快速解决、简单的答案,但这里有一些提示:好的。
干净的代码。能够解释一段代码行为的最重要的一点是,它写在一个容易理解的问题上。根据变量所代表的内容命名变量,根据方法的作用命名方法,在SOLID 中应用Single responsibility principle (S ):http://en.wikipedia.org/wiki/solid(面向对象的设计),这意味着每段代码都应该有一个单独的职责,并且不做其他任何事情。一旦您的代码是干净的,就可以更容易地解释它,也可以跨越多个代码层。对于混乱的代码,试图理解一个方法做了什么可能会让您忘记为什么要首先读取这个方法。(提示:阅读罗伯特·C·马丁的《清洁代码》)好的。
避免返回null 值。如果null 值会使程序无法正常运行,则抛出exception (确保添加适当的错误处理)。返回null 值可能是可接受的情况是尝试从数据库中获取对象。在这些情况下,编写处理null 值的代码,并在您的耳朵后面记下,这里有可能返回null 的内容。handle返回的null 值尽可能接近返回null 的方法的调用方(不要盲目地将其传递回调用链)。好的。
永远不要将显式的null 值作为参数传递(至少不要跨类传递)。如果您处于一个只有传递null 参数的位置,那么创建一个没有该参数的新方法就是可行的。好的。
(P)证实你的投入!2.Identify the"entry-points"to your application.他们可以从WebServices,Rest-Services,Remote EJB Classes,Controllers等任何地方获得这些进入点的方法,并要求你:"如果这种方法无效,是否会执行纠正措施?"如果答案是否定的,那就是0这将是一个EDOCX1,如果需要的参数是丢失。The good thing about this type of validation in the entry-points,is that you can they easily assume in the code being executed from the entry-point,that this variable will never be null!Also,if this fails,being a t the entry-point,debugging is made a lot easier than it would be made if you just got a EDOCX1 since a failure like this can only mean one thing:the client did't send you all the required information.在大多数情况下,你希望验证所有投入的参数,如果你确定自己在一个位置上,在你需要的地方,指定一个使用多种语言的三种语言的标语——标语,它可能是一个标志,一个Badly指定的接口,它需要对用户的需要进行反思/补充。好的,好的。
(P)当与EDOCX1一起工作的时候,第4个音标,返回一个帝国比零!好的,好的。
(P)当我与数据库一起工作时,我使用了EDOCX1In that way,you'll know that a value read from the database cannot be null,and you won't have to check for it.好的,好的。
(P)把你的法典和石刻在它上。既然如此,你就要对《守则》的行为作出保证,以便立即生效,如果所有对你适用的投入都是有效的,那么你就可以保证这些价值永远不会完全无效。好的,好的。
(P)如果你不愿意做这件事,你自己写的代码自动测试。通过写作测试,你将重新考虑你的法典,而你也将成为一个更可靠的信息,那就是它所支持的是什么。Also,automated tests guards you from blunders during refactoring,by letting you know immediatly that this piece of code is not doing what it used to.好的,好的。
(P)You still have to null-check of course,but it can trimmed down to the bare minimum(I.E.The situation where you know you might be getting a null-value,instead of everyywhere just to be sure.)When it comes to null-checks,I actually prefer to use the ternary operator(but use with care,when you start nesting them come really messary.)好的,好的。字母名称好吧。
可以定义UTIL方法,它以Java 8 LAMBDAS几乎完全的方式处理嵌套空校验。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51
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,您可以将供应商传递到下面的帮助器方法,
1 2 3
if ( CommonUtil.resolve ( ( ) -> a.b ( ) .c ( ) ) .isPresent ( ) ) {
}
上面替换了锅炉板代码,如下所示,
1 2 3
if ( a!= null && a.b ( ) != null && a.b ( ) .c ( ) != null ) {
}
//commonUtil.java
1 2 3 4 5 6 7 8
public static < T
> Optional
< T
> resolve
( Supplier
< T
> resolver
) {
try {
T result
= resolver.
get ( ) ;
return Optional.
ofNullable ( result
) ;
} catch ( NullPointerException var2
) {
return Optional.
empty ( ) ;
}
}
我们一直在使用Apache库(ApacheCommons)来解决这个问题。
1
ObjectUtils.equals ( object, null )
或
1
CollectionUtils.isEmpty ( myCollection) ;
或
1
StringUtils.isEmpty ( "string" ) ;
作为一种实践,我喜欢前面的回答,即为集合提供初始默认值或空集合,以最小化需求。
这些可以是简单的使用,防止您使用NullPointerException或使用空集合。这并不能回答如何处理空对象的问题,但可以对对象或集合的基本验证进行一些检查。
希望这有帮助。
我不喜欢CollectionUtils.isEmpty 和StringUtils.isEmpty ,因为它们也执行空检查,这不是方法名所暗示的。
我更喜欢这个
1 2 3 4
public void simpleFunc( SomeObject someObject) {
someObject = someObject != null ? someObject : new SomeObject( null ) ;
someObject.doSomething ( ) ;
}
当然,在我的示例中,有些对象会优雅地处理一个空参数。例如,记录这样的事件,不做更多的事情。
零安全性的Kotlin是优雅的选择,但它意味着更大的变化。
你有一个选择
得到
1 2 3
[ ERROR
] found
: null
[ ERROR
] required
: @Initialized @NonNull
Boolean
[ ERROR
] -> [ Help
1 ]
还有其他方法,如使用Java 8的可选的、番石榴注释、空对象模式等,只要你获得了避免的目标就没关系了!=空
空对象模式可以用作此问题的解决方案。为此,应该修改someObject的类。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
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 ;
}
}
现在是检查的开始,
1 2 3
if ( someobject != null ) {
someobject.doCalc ( ) ;
}
我们可以使用,
1 2 3
if ( ! someObject.isNil ( ) ) {
someobject.doCalc ( ) ;
}
参考:https://www.tutorialspoint.com/design_pattern/null_object_pattern.htm
你只是在修改支票。没有真正的好处。尽管我们付出了额外的努力来实现建议的模式,但是代码仍然不干净。
您可以使用JUnit这样的框架将类与单元测试结合起来。这样,您的代码将是干净的(没有无用的检查),并且您将确保您的实例不会为空。
这是使用单元测试的一个很好的理由。
对于实用程序类,可以检查参数是否不为空。
在所有其他情况下,您可能不必这样做。尽可能多地使用封装,从而减少您想要检查空值的地方。
函数方法可能有助于包装重复的空检查并执行匿名代码,如下面的示例。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
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
; } ) ;
这是我的方法……
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34
class MyObjectHandler
{
public static final int EXCEPTION
= ( - 3 ) ;
public static final int INACCESSIBLE
= ( - 2 ) ;
public static int doSomething
( MyObject obj, MyObjectParameter
[ ] input
)
{
int returnValue
= 0 ;
try
{
if ( obj
!= null )
{
returnValue
= obj.
doSomething ( input
) ;
}
else
{
returnValue
= MyObjectHandler.
INACCESSIBLE ;
}
}
catch ( Exception e
)
{
e.
printStack ( ) ;
returnValue
= MyObjectHandler.
EXCEPTION ;
}
finally
{
return returnValue
;
}
}
..
}
那么您的代码将类似于:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
import xx.xx.xx.MyObjectHandler ;
import xx.xx.xx.MyObjectParameter ;
class Test
{
public static void main ( )
{
MyObject obj = null ;
MyObjectHandler.doSomething ( obj, null ) ;
}
..
}
另一个建议是进行防御性编程——在这种情况下,类/函数提供已知和安全的默认值,并且为真正的错误/异常保留空值。
例如,当出现问题(比如将数字转换为字符串)时,函数不返回返回空字符串的函数,而是返回空字符串(")。在继续之前,您仍然需要测试返回值,但不会出现异常的特殊情况。这种编程方式的另一个好处是,您的程序能够区分正常操作和异常,并相应地作出响应。
- 1。更糟的是。现在,如果忘记测试,程序不会崩溃,而是静默地传播不正确的值。
下载groovy,设置groovy,下载groovy Eclipse插件,并将file.java更改为file.groovy。然后:
1 2 3 4 5
String u
;
if ( u
) {
} //Cast auto to false,
else {
}
就像php/javascript一样。然后继续用普通的Java编程,它是Java++。
"就像php/javascript"~这是我想要避免的。
"躲避"!=通过安装Groovy在NULL"JAVA语句"中…多酷啊!
问题是"避免Java中的空语句",这是一个完整的解决问题的方法,所以非常酷!!!!!!!!!!!!!!!!!
@萨曼达什~我不知道你的问题是什么,但如果你想成为一名专业人士,你需要冷静下来,毫无态度地讨论这个话题。无聊的。
有问题!=空是什么?你需要使用Java,但是为了做到这一点,你必须不断避免NulLoPoExtExcExts,而且你的代码到处都是重复的。=使代码难以读取的空检查。我的解决方案是使用Groovy,它允许人们保留当前的Java代码库。你的评论不懂代码。我建议你删掉它,喜欢你评论的8+ppl应该为自己感到羞耻。
使用
1 2 3
if ( null != someobject) {
someobject.doCalc ( ) ;
}
它不会引发空指针异常