我经常使用object != null
来避免NullPointerException
。
有没有好的替代方法?
例如:
if (someobject != null) {
someobject.doCalc();
}
如果不知道对象是否为null
,则可以避免NullPointerException
。
请注意,接受的答案可能已过期,请参阅https://stackoverflow.com/a/2386013/12943以获取最新的方法。
#1楼
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");
}
#2楼
最终,完全解决此问题的唯一方法是使用另一种编程语言:
- 在Objective-C中,您可以等效于在
nil
上调用方法,并且绝对不会发生任何事情。 这使得大多数null检查都是不必要的,但会使错误更难以诊断。 - 在Java派生的语言Nice中 ,有两种所有类型的版本:可能为空的版本和非为空的版本。 您只能在非空类型上调用方法。 通过显式检查null,可以将潜在为null的类型转换为非null的类型。 这样可以更轻松地知道哪些地方需要空检查,哪些地方没有空检查。
#3楼
只是永远不要使用null。 不要这样
在我的课程中,大多数字段和局部变量都具有非null的默认值,并且我在代码的各处添加了合同声明(始终为断言),以确保这是强制执行的(因为它更简洁,比让它更具表达力)作为NPE出现,然后必须解析行号等)。
一旦采用了这种做法,我发现问题似乎已经解决。 您会在开发过程中很早就偶然发现问题,并且意识到自己的弱点。更重要的是,它有助于封装不同模块的关注点,不同模块可以“相互信任”,而不会产生混乱。 if = null else
代码if = null else
构造!
从长远来看,这是防御性的编程,可以使代码更简洁。 始终对数据进行消毒,例如在此处执行严格的标准,这样问题就会消失。
class C {
private final MyType mustBeSet;
public C(MyType mything) {
mustBeSet=Contract.notNull(mything);
}
private String name = "<unknown>";
public void setName(String s) {
name = Contract.notNull(s);
}
}
class Contract {
public static <T> T notNull(T t) { if (t == null) { throw new ContractException("argument must be non-null"); return t; }
}
合同就像微型单元测试一样,即使在生产中也总是运行,并且当发生故障时,您知道为什么,而不是随机的NPE,您必须以某种方式弄清楚。
#4楼
Java 8附带了新的java.util.Optional
类,可以解决一些问题。 至少可以说这提高了代码的可读性,并且在使用公共API的情况下,API合同对于客户开发人员而言更加清晰。
他们像这样工作:
给定类型的一个可选对象( Fruit
)被创建为方法的返回类型。 它可以为空或包含一个Fruit
对象:
public static Optional<Fruit> find(String name, List<Fruit> fruits) {
for (Fruit fruit : fruits) {
if (fruit.getName().equals(name)) {
return Optional.of(fruit);
}
}
return Optional.empty();
}
现在看这段代码,我们搜索的列表, Fruit
( 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");
当然,仍然需要检查空值/空值,但是至少开发人员意识到该值可能为空,并且忘记检查的风险受到限制。
在使用Optional
从头构建的API中,无论何时返回值可能为空,并且仅当它不能为null
(约定)时才返回普通对象,客户端代码可能会放弃对简单对象返回值的null检查...
当然, Optional
也可以用作方法参数,在某些情况下,这可能是比5或10个重载方法更好的指示可选参数的方法。
Optional
提供其他方便的方法,如orElse
,允许使用默认值,并ifPresent
与工作lambda表达式 。
我邀请您阅读本文(我写此答案的主要来源),其中很好地解释了有问题的NullPointerException
(通常是空指针)以及Optional
带来的(部分)解决方案: Java Optional Objects 。
#5楼
- 切勿将变量初始化为null。
- 如果无法使用(1),请将所有集合和数组初始化为空的集合/数组。
用您自己的代码执行此操作,可以避免!=空检查。
大多数时候,空检查似乎可以保护集合或数组的循环,因此只需将它们初始化为空,就不需要任何空检查。
// Bad
ArrayList<String> lemmings;
String[] names;
void checkLemmings() {
if (lemmings != null) for(lemming: lemmings) {
// do something
}
}
// Good
ArrayList<String> lemmings = new ArrayList<String>();
String[] names = {};
void checkLemmings() {
for(lemming: lemmings) {
// do something
}
}
这样做的开销很小,但是对于更简洁的代码和更少的NullPointerExceptions而言,这是值得的。
#6楼
如果不允许使用未定义的值:
您可以将IDE配置为警告您有关潜在的空解除引用。 例如,在Eclipse中,请参见首选项> Java>编译器>错误/警告/空分析 。
如果允许使用未定义的值:
如果要在有意义的地方定义新的API ,请使用“ 选项模式” (功能语言可能很熟悉)。 具有以下优点:
- API中明确说明了输入或输出是否存在。
- 编译器会强制您处理“未定义”的情况。
- Option是monad ,因此不需要冗长的null检查,只需使用map / foreach / getOrElse或类似的组合器即可安全地使用该值(示例) 。
Java 8具有内置的Optional
类(推荐); 对于早期版本,有一些库替代方案,例如Guava的Optional
或FunctionalJava的Option
。 但是,就像许多功能样式的模式一样,在Java中使用Option(甚至8)会产生很多样板,您可以使用不太冗长的JVM语言(例如Scala或Xtend)来减少这种样板。
如果必须处理可能返回null的API,则在Java中不能做太多事情。 Xtend和Groovy具有Elvis运算符 ?:
和空安全解除引用运算符 ?.
,但请注意,如果引用为null,则返回null,因此它只是“推迟”了null的正确处理。
#7楼
我可以更一般地回答吗!
当方法以非预期的方式获取参数时,我们通常会遇到此问题(错误的方法调用是程序员的错)。 例如:您期望获得一个对象,而是获得一个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中实现此模式,可以使用诸如javax.annotation.NotNull之类的核心Java批注,也可以使用诸如Hibernate Validator之类的更复杂的库。
只是一个示例:
getCustomerAccounts(@NotEmpty String customerId,@Size(min = 1) String accountType)
现在,您可以安全地开发方法的核心功能,而无需检查输入参数,它们可以保护您的方法免受意外参数的影响。
您可以更进一步,并确保在您的应用程序中只能创建有效的pojos。 (来自休眠验证器站点的示例)
public class Car {
@NotNull
private String manufacturer;
@NotNull
@Size(min = 2, max = 14)
private String licensePlate;
@Min(2)
private int seatCount;
// ...
}
#8楼
对于大多数开发人员来说,这是最常见的错误。
我们有多种方法可以解决此问题。
方法1:
org.apache.commons.lang.Validate //using apache framework
notNull(对象对象,字符串消息)
方法二:
if(someObject!=null){ // simply checking against null
}
方法3:
@isNull @Nullable // using annotation based validation
方法4:
// by writing static method and calling it across whereever we needed to check the validation
static <T> T isNull(someObject e){
if(e == null){
throw new NullPointerException();
}
return e;
}
#9楼
我高度无视建议在每种情况下使用null对象的答案。 这种模式可能破坏合同,并越来越深地掩埋问题,而不是解决它们,更不用说使用不当会创建另一堆样板代码,需要将来进行维护。
实际上,如果从方法返回的内容可以为null,并且调用代码必须对此做出决定,则应该有一个更早的调用来确保状态。
还请记住,如果不加小心使用,空对象模式将占用大量内存。 为此-NullObject的实例应在所有者之间共享,而不是每个所有者的唯一实例。
我也不建议使用这种模式,在这种模式中类型应为原始类型表示形式-像不是标量的数学实体:向量,矩阵,复数和POD(普通旧数据)对象,它们应持有状态以Java内置类型的形式。 在后一种情况下,您最终将以任意结果调用getter方法。 例如,NullPerson.getName()方法应返回什么?
值得考虑这样的情况以避免产生荒谬的结果。
#10楼
如果使用(或计划使用) JetBrains IntelliJ IDEA , Eclipse或Netbeans等Java IDE或findbugs之类的工具,则可以使用注释来解决此问题。
基本上,您有@Nullable
和@NotNull
。
您可以使用in方法和参数,如下所示:
@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
,他们添加了对其他@Nullable
@NotNull
实现的支持。
请参阅博客文章更灵活和可配置的@ Nullable / @ NotNull批注 。
#11楼
您可能会考虑空对象是一个错误的情况,而不是空对象模式(它有其用途)。
引发异常后,检查堆栈跟踪并解决该错误。
#12楼
- 如果您认为一个对象不应该为null(或者它是一个bug),请使用断言。
- 如果您的方法不接受空参数,请在javadoc中说出来并使用断言。
仅当您要处理对象可能为null的情况时,才需要检查对象!= null。
有人建议在Java7中添加新的注释,以帮助处理null / notnull参数: http ://tech.puredanger.com/java7/#jsr308
#13楼
有时,您有对其定义对称操作的参数进行操作的方法:
a.f(b); <-> b.f(a);
如果您知道b永远不能为null,则可以交换它。 对于equals最为有用:代替foo.equals("bar");
最好做"bar".equals(foo);
。
#14楼
根据您要检查的对象的类型,您也许可以使用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类只是众多类之一。 公共领域中有很多不错的类可以进行空安全操作。
以下是一个示例,说明了在包含apache库(commons-lang-2.4.jar)时如何在JAVA中使用空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(org.springframework.util.Assert)使用此静态类的示例
Assert.notNull(validationEventHandler,"ValidationHandler not Injected");
#15楼
如果不允许使用空值
如果您的方法是从外部调用的,则应从以下内容开始:
public void method(Object object) {
if (object == null) {
throw new IllegalArgumentException("...");
}
然后,在该方法的其余部分中,您将知道object
不为null。
如果它是一个内部方法(不是API的一部分),只需证明它不能为null即可。
例:
public String getFirst3Chars(String text) {
return text.subString(0, 3);
}
但是,如果您的方法仅将值传递给它,而下一个方法将其传递给等等,则可能会出现问题。 在这种情况下,您可能需要检查上面的参数。
如果允许为null
这真的取决于。 如果发现我经常这样做:
if (object == null) {
// something
} else {
// something else
}
因此,我分支了,做了两件事完全不同。 没有丑陋的代码片段,因为我确实需要根据数据做两件事。 例如,我应该在输入上工作,还是应该计算一个好的默认值?
实际上,我很少使用“ if (object != null && ...
”。
如果您显示了通常使用该惯用法的示例,则可能更容易为您提供示例。
#16楼
提出该问题指出您可能对错误处理策略感兴趣。 您的团队的架构师应决定如何处理错误。 做这件事有很多种方法:
允许异常波及-在“主循环”或其他一些管理例程中捕获异常。
- 检查错误情况并适当处理
当然也可以看看面向方面的编程-它们有巧妙的方法将if( o == null ) handleNull()
插入您的字节码。
#17楼
在我看来,这似乎是一个相当普遍的问题,初级和中级开发人员往往会在某个时候遇到这些问题:他们要么不知道,要么不信任他们所参与的合同,并且防御性地检查了null。 另外,在编写自己的代码时,他们倾向于依靠返回空值来表示某些内容,因此要求调用者检查空值。
换句话说,在两种情况下会出现空检查:
如果为null,则表示合同中的有效回复; 和
无效的地方。
(2)容易。 使用assert
语句(断言)或允许失败(例如NullPointerException )。 断言是1.4中新增的一个未被充分利用的Java功能。 语法为:
assert <condition>
要么
assert <condition> : <object>
其中, <condition>
是布尔表达式, <object>
是一个对象,其toString()
方法的输出将包含在错误中。
如果条件不成立,则assert
语句将引发Error
( AssertionError
)。 默认情况下,Java会忽略断言。 您可以通过将选项-ea
传递给JVM来启用断言。 您可以启用和禁用单个类和程序包的断言。 这意味着尽管我的测试几乎没有显示断言对性能的影响,但是您可以在开发和测试时使用断言来验证代码,并在生产环境中禁用它们。
在这种情况下,不使用断言是可以的,因为代码只会失败,这就是使用断言时会发生的情况。 唯一的区别是,有了断言,它可能会更早地发生,以更有意义的方式出现,并可能带有额外的信息,这可以帮助您弄清楚为什么不期望它会发生。
(1)有点难。 如果您无法控制正在调用的代码,那么您将陷入困境。 如果null为有效响应,则必须检查它。
但是,如果您控制的是代码(通常是这种情况),那就是另一回事了。 避免使用null作为响应。 使用返回集合的方法很容易:几乎总是返回空集合(或数组)而不是null。
使用非集合,可能会更困难。 以这个为例:如果您具有以下接口:
public interface Action {
void doSomething();
}
public interface Parser {
Action findAction(String userInput);
}
在Parser中,原始的用户输入会找到要执行的操作,如果您正在为某些操作实现命令行界面的话。 现在,如果没有适当的操作,您可以使合同返回null。 这导致您正在谈论的空检查。
另一种解决方案是从不返回null,而使用Null Object模式 :
public class MyParser implements Parser {
private static Action DO_NOTHING = new Action() {
public void doSomething() { /* do nothing */ }
};
public Action findAction(String userInput) {
// ...
if ( /* we can't find any actions */ ) {
return DO_NOTHING;
}
}
}
比较:
Parser parser = ParserFactory.getParser();
if (parser == null) {
// now what?
// this would be an example of where null isn't (or shouldn't be) a valid response
}
Action action = parser.findAction(someInput);
if (action == null) {
// do nothing
} else {
action.doSomething();
}
至
ParserFactory.getParser().findAction(someInput).doSomething();
这是一个更好的设计,因为它可以导致代码更简洁。
也就是说,对于findAction()方法来说,抛出带有有意义的错误消息的Exception异常是完全适当的-特别是在这种情况下,您依赖于用户输入。 对于findAction方法抛出一个异常,比对一个没有解释的简单NullPointerException进行抛出的调用方法要好得多。
try {
ParserFactory.getParser().findAction(someInput).doSomething();
} catch(ActionNotFoundException anfe) {
userConsole.err(anfe.getMessage());
}
或者,如果您认为try / catch机制太丑陋,而不是什么都不做,则默认操作应向用户提供反馈。
public Action findAction(final String userInput) {
/* Code to return requested Action if found */
return new Action() {
public void doSomething() {
userConsole.err("Action not found: " + userInput);
}
}
}
#18楼
仅在这种情况下-
在调用equals方法之前不检查变量是否为null(下面的字符串比较示例):
if ( foo.equals("bar") ) {
// ...
}
如果foo
不存在,将导致NullPointerException
。
如果像这样比较String
,则可以避免这种情况:
if ( "bar".equals(foo) ) {
// ...
}
#19楼
我已经尝试过NullObjectPattern
但对我而言并不总是最好的方法。 有时有时候“不采取行动”是不合适的。
NullPointerException
是运行时异常 ,这意味着它是开发人员的错误,并且有足够的经验,它可以告诉您错误的确切位置。
现在来回答:
尝试使所有属性及其访问者尽可能地私有,或者完全避免将其公开给客户端。 您当然可以在构造函数中使用参数值,但是通过减小范围,您不会让客户端类传递无效值。 如果需要修改值,则始终可以创建一个新object
。 您只需检查构造函数中的值一次,而在其余方法中,几乎可以确定这些值不为null。
当然,经验是理解和应用此建议的更好方法。
字节!
#20楼
对于每个Java开发人员来说,这都是一个非常普遍的问题。 因此,Java 8在没有混乱代码的情况下提供了官方支持来解决这些问题。
Java 8引入了java.util.Optional<T>
。 它是一个可能包含也可能不包含非null值的容器。 Java 8提供了一种更安全的方法来处理在某些情况下其值可能为null的对象。 它受Haskell和Scala的思想启发。
简而言之,Optional类包含一些方法来显式处理存在或不存在值的情况。 但是,与null引用相比,优点是Optional <T>类迫使您考虑不存在该值的情况。 因此,您可以防止意外的空指针异常。
在上面的示例中,我们有一个家庭服务工厂,该工厂将句柄返回给家庭中可用的多个设备。 但是这些服务可能不可用,也不可用。 这意味着它可能会导致NullPointerException。 不用在使用任何服务之前添加null if
条件,而是将其包装到Optional <Service>中。
包装到选项<T>
让我们考虑一种从工厂获取服务引用的方法。 而不是返回服务引用,而是将其包装为Optional。 它使API用户知道退回的服务可能会或可能不会可用/无法正常运行
public Optional<Service> getRefrigertorControl() {
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如果给定的Consumer是非null值,则使用引用来调用它。 否则,它什么都不做。
@FunctionalInterface
public interface Consumer<T>
表示一个接受单个输入参数且不返回结果的操作。 与大多数其他功能接口不同,消费者应该通过副作用来操作。 它是如此干净,易于理解。 在上面的代码示例中,如果Optional保持引用为非null,则调用HomeService.switchOn(Service)
。
我们经常使用三元运算符检查空条件并返回替代值或默认值。 Optional提供了另一种无需检查null即可处理相同条件的方法。 Optional.orElse(defaultObj)如果Optional具有null值,则返回defaultObj。 让我们在示例代码中使用它:
public static Optional<HomeServices> get() {
service = Optional.of(service.orElse(new HomeServices()));
return service;
}
现在,HomeServices.get()可以执行相同的操作,但是效果更好。 它检查服务是否已经初始化。 如果是,则返回相同值或创建新的新服务。 可选的<T> .orElse(T)有助于返回默认值。
最后,这是我们的NPE以及空免检代码:
import java.util.Optional;
public class HomeServices {
private static final int NOW = 0;
private static Optional<HomeServices> service;
public static Optional<HomeServices> get() {
service = Optional.of(service.orElse(new HomeServices()));
return service;
}
public Optional<Service> getRefrigertorControl() {
Service s = new RefrigeratorService();
//...
return Optional.ofNullable(s);
}
public static void main(String[] args) {
/* Get Home Services handle */
Optional<HomeServices> homeServices = HomeServices.get();
if(homeServices != null) {
Optional<Service> refrigertorControl = homeServices.get().getRefrigertorControl();
refrigertorControl.ifPresent(HomeServices::switchItOn);
}
}
public static void switchItOn(Service s){
//...
}
}
完整的帖子是NPE以及Null免提代码…真的吗? 。
#21楼
除了使用assert
之外,还可以使用以下内容:
if (someobject == null) {
// Handle null here then move on.
}
这比以下要好一些:
if (someobject != null) {
.....
.....
.....
}
#22楼
对于Java 8或更高版本,最好的替代方法是使用Optional
类。
Optional stringToUse = Optional.of("optional is there");
stringToUse.ifPresent(System.out::println);
这对于可能为空值的长链特别方便。 例:
Optional<Integer> i = Optional.ofNullable(wsObject.getFoo())
.map(f -> f.getBar())
.map(b -> b.getBaz())
.map(b -> b.getInt());
如何在null上引发异常的示例:
Optional optionalCarNull = Optional.ofNullable(someNull);
optionalCarNull.orElseThrow(IllegalStateException::new);
Java 7引入了Objects.requireNonNull
方法,该方法在检查某些内容是否为非空时非常方便。 例:
String lowerVal = Objects.requireNonNull(someVar, "input cannot be null or empty").toLowerCase();
#23楼
public static <T> T ifNull(T toCheck, T ifNull) {
if (toCheck == null) {
return ifNull;
}
return toCheck;
}
#24楼
Google Collections框架提供了一种实现null检查的好方法。
库类中有一个这样的方法:
static <T> T checkNotNull(T e) {
if (e == null) {
throw new NullPointerException();
}
return e;
}
用法是(使用import static
):
...
void foo(int a, Person p) {
if (checkNotNull(p).getAge() > a) {
...
}
else {
...
}
}
...
或在您的示例中:
checkNotNull(someobject).doCalc();
#25楼
哇,当我们有57种不同的方法推荐NullObject pattern
时,我几乎不愿意添加另一个答案,但是我认为对这个问题感兴趣的某些人可能想知道在Java 7桌上有一个建议添加“ null” -“安全处理”-if -not-equal-null逻辑的简化语法。
Alex Miller给出的示例如下所示:
public String getPostcode(Person person) {
return person?.getAddress()?.getPostcode();
}
?.
表示仅在左标识符不为null时才取消引用,否则将表达式的其余部分评估为null
。 有些人,例如Java Posse成员Dick Wall和Devoxx的选民确实喜欢这个提议,但也有人反对,理由是它实际上会鼓励更多地使用null
作为定点值。
更新:在Project Coin下已提交了有关Java 7中的空安全运算符的正式建议 。 语法与上面的示例略有不同,但是是相同的概念。
更新:空安全运算符建议未纳入Project Coin。 因此,您不会在Java 7中看到此语法。
#26楼
确实是Java中常见的“问题”。
首先,我对此有以下想法:
我认为在NULL不是有效值的情况下传递NULL时“吃掉”东西是不好的。 如果您没有因某种错误而退出该方法,则意味着您的方法没有出现任何错误,这是不正确的。 然后,在这种情况下,您可能会返回null,然后在接收方法中再次检查null,并且它永远不会结束,最终会出现“ if!= null”等。
因此,恕我直言,null必须是一个严重错误,它会阻止进一步执行(也就是说,null不是有效值)。
我解决此问题的方法是:
首先,我遵循以下约定:
- 所有公共方法/ API始终检查其参数是否为null
- 由于所有私有方法都是受控方法,因此它们不检查是否为空(如果上面没有处理,请让它们与nullpointer异常一起死亡)
- 唯一不检查是否为空的方法是实用程序方法。 它们是公共的,但是如果出于某种原因调用它们,则知道您传递了哪些参数。 这就像试图在不提供水的情况下在水壶中烧开水一样。。。
最后,在代码中,public方法的第一行是这样的:
ValidationUtils.getNullValidator().addParam(plans, "plans").addParam(persons, "persons").validate();
请注意,addParam()返回self,因此您可以添加更多参数进行检查。
如果任何参数为null(选中或未选中更多是设计/味道问题,但我的ValidationException
已选中),则validate()
方法将抛出checked ValidationException
。
void validate() throws ValidationException;
例如,如果“计划”为空,则该消息将包含以下文本:
“ 参数[plans]遇到非法的参数值null ”
如您所见,用户消息需要addParam()方法(字符串)中的第二个值,因为即使通过反射也无法轻松检测传入的变量名(无论如何,都不是本文的主题...)。
是的,我们知道,在这一行之外,我们将不再遇到空值,因此我们可以安全地调用这些对象上的方法。
这样,代码是干净的,易于维护的和可读的。
#27楼
我喜欢“快速失败”代码。 问问自己-在参数为null的情况下,您是否正在做一些有用的事情? 如果在这种情况下您对代码应该做的事情没有明确的答案...即,一开始它永远不应该为null,然后忽略它并允许引发NullPointerException。 调用代码对NPE的意义与IllegalArgumentException一样,但对于开发人员来说,调试和了解如果抛出NPE会导致问题出在哪里比在代码中尝试执行其他意外情况要容易得多逻辑-最终导致应用程序无论如何都失败。
#28楼
我喜欢Nat Pryce的文章。 这里是链接:
在文章中,还有一个指向Java Maybe Type的Git存储库的链接,我觉得很有趣,但是我认为单独使用它可能不会减少检查代码的膨胀。 在Internet上进行了一些研究之后,我认为!=空代码膨胀主要可以通过精心设计来减少。
#29楼
空不是一个“问题”。 它是完整的建模工具集的组成部分。 软件旨在模拟世界的复杂性,而null承担了它的负担。 Null表示 Java等中的“无数据”或“未知” 。 因此,将空值用于这些目的是适当的。 我不喜欢“空对象”模式; 我认为这引起了“ 谁来守护监护人 ”的问题。
如果你问我我女朋友的名字是什么,我会告诉你我没有女朋友。 在Java语言中,我将返回null。 一种替代方法是抛出有意义的异常,以指示一些无法(或不希望)在那里解决的问题,并将其委托给堆栈中更高的位置,以重试或向用户报告数据访问错误。
对于“未知问题”,请提供“未知答案”。 (从业务的角度来看这是正确的,请保持null安全)在使用方法之前在方法内检查一次null,以免多个调用者在调用之前进行检查。
public Photo getPhotoOfThePerson(Person person) { if (person == null) return null; // Grabbing some resources or intensive calculation // using person object anyhow. }
Previous导致正常的逻辑流程,无法从我的照片库中获取不存在的女友的照片。
getPhotoOfThePerson(me.getGirlfriend())
并且它与新的Java API兼容(展望未来)
getPhotoByName(me.getGirlfriend()?.getName())
虽然找不到某人存储在数据库中的照片是“正常的业务流程”,但在某些其他情况下,我曾经使用如下所示的对
public static MyEnum parseMyEnum(String value); // throws IllegalArgumentException public static MyEnum parseMyEnumOrNull(String value);
并且不要讨厌键入
<alt> + <shift> + <j>
(在Eclipse中生成javadoc)并为您的公共API编写另外三个单词。 对于那些不阅读文档的人来说,这已经足够了。/** * @return photo or null */
要么
/** * @return photo, never null */
这是理论上的情况,在大多数情况下,您应该首选Java null安全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 Null-safe API在此处更合适,可以有选择地指定在程序员错误的情况下隐式初始化和不隐式初始化的内容 。问:可以执行冗余代码,并可以获取不必要的资源。
getPhotoByName()
如果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
的子类。 当ThreadDeath
为Error
时,我们在Java历史上已有先例,这不是因为它实际上是应用程序错误,而仅仅是因为它不希望被捕获! 比ThreadDeath
更适合作为Error
NPE! 但事实并非如此。仅当业务逻辑暗示时才检查“无数据”。
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将堆栈跟踪打印到系统err。 应该监视应用程序错误日志,并为每个未处理的异常打开Jira问题,实际上这是应用程序错误。 程序员应在初始化工作中的某处修复错误。
#30楼
Guava是Google非常有用的核心库,它具有避免空值的漂亮且有用的API。 我发现UsingAndAvoidingNullExplained非常有帮助。
如Wiki中所述:
Optional<T>
是用非空值替换可空T引用的一种方法。 Optional可以包含非空T引用(在这种情况下,我们称该引用为“存在”),也可以不包含任何内容(在这种情况下,我们称该引用为“不存在”)。 从来没有说过“包含null”。
用法:
Optional<Integer> possible = Optional.of(5);
possible.isPresent(); // returns true
possible.get(); // returns 5