2

涉及到对象操作 object.xxxx(),如果对象为null则会抛异常,如果每次都if(o !=null) 代码很丑陋

一种做法是创建对象或者方法返回对象时都new一个对象而不是返回null
不过这样有时也不太好,比如结果为空的时候返回null更合理一点

有更好的建议么 ?

ddcz 46
2012-08-28 提问
14 个回答
6

已采纳

这是是工程设计上常见的问题,一般的解决方案就是 @特价萝卜 所说的NullObject模式。本来想投 @特价萝卜 的答案赞成票,但纠结良久还是觉得说得有些复杂了。

简单来说就是有一个NullObject与原有业务对象实现相同的接口(或继承同一个父类),让客户端调用时可以无感知(也不必判定null)

有一个很好的例子就是著名JSON解析框架Jackson。以下代码是Jackson从一段JSON中获取其一级子节点lv1下的二级子节点lv2的内容:

JsonNode root = ...;
JsonNode child = root.get("lv1").get("lv2");

以上代码很可能遇到lv1不存在的情况,因此第一个get()就会返回null,那么第二个get()执行时自然就抛出NullPointerException了。为了解决这个问题,作者提供了path方法来替代get方法:

JsonNode root = ...;
JsonNode child = root.path("lv1").path("lv2");

当lv1不存在时,path()返回一个JsonNode的子类叫做MissingNode(但客户端暂时无需知道),MissingNode的path方法则继续返回MissingNode,这样无论这个链式调用写多长都不会抛出任何异常。

直到最后客户端调用完成后检查返回结果是否为MissingNode:

if (child.isMissingNode()) { ... }
3

有一种模式叫NullObject,意思就是建立一个专用的空对象,以此来代表结果为空。比如这样:

public interface MyObj {
  public String someOp();
}

public class NullObj implements MyObj {
  public String someOp() {
    return "It's a content of NullObj"; // 或者抛出一个特定异常,又或者打印出一条警告信息
  }
}

public class NormalObj implements MyObj {
  public String someOp() {
    String result = null;
    // 执行一些正常的代码
    return result;
  }
}

用法:

1、建立全局常量:

public enum Constants { //这种写单例的方式在《Effective Java》中被推荐过,非常好的实现方式。
  ONE;
  public final MyObj NULL_OBJ = new NullObj(); //如果用enum写单例的话这里就不用static修饰符了。
}

2、被调用的代码在条件符合的时候返回NullObj:

public class Server {
...
  public MyObj receive(String param) {
    if (param == null) {
      System.out.println("..."); //记录一些log
      return Constants.ONE.NULL_OBJ;
    }
    MyObj result = null;
    ...
    return result;
  }
...
}

3、在调用方:

public class Client {
  public result send(String content) {
    Server server = null;
    String result = null;
    try {
      ... //初始化server
      MyObj myObj = server.receive(content);
      result = myObj.someOp(); //如果你前面选择的是抛出特定异常,则这行代码就必须放在try-catch语句块中了
      // 后续的操作就非常灵活了,或比对结果字符串或catch特定异常,取决于你的NullObj是怎么实现的
      ...
    } catch (Exception e) {
      ... 
    }
  }
}

当然,通过捕获异常来进行流程控制的方式是不被推荐的。NullObject很灵活,可以做出很多扩展性很强的实现方案。

1

2013年了, 考虑一下 java7的Objects.requireNonNull()方法? http://docs.oracle.com/javase/7/docs/api/java/util/Objects.html#requireNonNull%28T%29

1

嗯,不错的选择。我的项目里底层封装的isObjectNull就采用的public static <T> T requireNonNull(T obj, String message)

Vin_Tsie · 2013-09-04

展开评论
0

1. 如果被调用方法返回可能为null的话,建议每次使用 if(null == o){ //异常分支处理 } 然后再进行正常分支的逻辑代码编写,这样就不会使代码多层条件判断嵌套;代码会美观一些。
2. 一般对于复杂的调用建议返回结果对象,这样可以封装被调用方法需要返回的各种异常状态等信息,便于调用端精细化处理。

0

三元运算符

0

返回为null时改为抛出异常不就好了。。。

0

你可以试试看用underscore.js,我一般判断的方法就是

if (!_.isNull(object.xxx()){
//非null,继续执行
}

0

apache 的commons 中有一些工具类可以使用

0

在Java的工程实践中,需要经常判断对象是否为null是一个常见都困扰,根据个人经验,我通常不做任何的判断,任其抛出NullPointerException。因为方法或者构造函数中允许参数为null通常并不是一个好的工程设计。或许有人会以为了增加系统的容错性,所以允许null参数来反驳,这也有一定的道理,不过这还是取决于你的类设计。我的经验是,通常要将你所设计的类划分职责,越是倾向于底层逻辑、基础数据操作的类,可以适当的容忍null参数;越是倾向于重要业务逻辑的类,通常不容忍null参数。在处理NullPointerException或者其他RuntimeException的时候,应该有一个统一的错误处理策略。就像Spring中对于Exception的策略一样,所有的方法都抛出RuntimeException。

多说一句,这个话题其实涉及到如何设计你的系统中的错误处理机制。这个绝对是见仁见智的事情,不会有一个绝对正确的答案。

0

assert o != null ; 不知道可不可行?

0

把null判断交给调用方。

0

参见guava中的optional

0
public boolean isNullObject(){
        boolean isNull=true;//默认为空
        Field[] at = this.getClass().getDeclaredFields();
        for (Field field : at) {
            field.setAccessible(true);
            try {
                if (field.get(this) != null){
                    isNull=false;//只要有一个属性非空,那就认为非空了
                    break;
                }
            } catch (Exception e) {

            } 
        }
        return isNull;
    }
-1

改用Scala吧!

撰写答案

推广链接