什么是番石榴checkNotNull点

我对番石榴很新鲜(说实话,我不是“新人”,我是一个完整的菜鸟),所以我决定阅读一些文档,在阅读时感到非常惊讶:

com.google.common.base.Preconditions.checkNotNull(…)

我不明白这个方法的意义。 这意味着,而不是做:

myObject.getAnything(); 

(如果myObject为null,可能会导致NullPointerException)

我应该使用

 checkNotNull(myObject).getAnything(); 

如果myObject为null, 则会抛出NullPointerException,如果不为null,则返回myObject。

我感到困惑,这可能是最愚蠢的问题,但…

这是什么意思? 在我能想到的任何情况下,这两行与结果完全相同。

我甚至不认为后者更具可读性。

所以我一定会错过一些东西。 它是什么?

这个想法是快速失败。 例如,考虑这个愚蠢的类:

 public class Foo { private final String s; public Foo(String s) { this.s = s; } public int getStringLength() { return s.length(); } } 

假设你不想让s为空值。 (否则getStringLength将抛出一个NPE)。 就目前来说,当你赶上那个时候,已经太晚了,很难找出是谁把它放在那里。 罪魁祸首可能完全是一个完全不同的阶级,而Foo实例可能早就build立起来了。 现在你必须梳理你的代码库,找出谁可能在那里放置一个null值。

相反,想象这个构造函数:

 public Foo(String s) { this.s = checkNotNull(s); } 

现在,如果有人在那里放置一个null ,你会马上发现 -​​ 而且你会有堆栈跟踪指出你到了错误的调用。


另一次这可能是有用的,如果你想要检查参数,然后再采取可以修改状态的操作。 例如,考虑这个类来计算它得到的所有string长度的平均值:

 public class StringLengthAverager { private int stringsSeen; private int totalLengthSeen; public void accept(String s) { stringsSeen++; totalLengthSeen += s.length(); } public double getAverageLength() { return ((double)totalLengthSeen) / stringsSeen; } } 

调用accept(null)将导致NPE被抛出,但不会在stringsSeen递增之前抛出。 这可能不是你想要的; 作为类的用户,我可以预料,如果它不接受空值,那么如果你传递一个null(换句话说,调用应该失败,但不应该使对象无效),它的状态应该是不变的。 显然,在这个例子中,你也可以通过在增加stringsSeen之前获取s.length()修复它,但是你可以看到如何使用一个更长和更复杂的方法,首先检查你的所有参数是否有效,只有修改状态:

  public void accept(String s) { checkNotNull(s); // that is, s != null is a precondition of the method stringsSeen++; totalLengthSeen += s.length(); } 

myObject.getAnything(); (如果myObject为null,可能会导致NullPointerException)

没有…只要myObject == null 就会抛出NPE。 在Java中,没有机会调用带有null接收方的方法(理论上的例外是静态方法,但是它们可以并且应该总是被调用而没有任何对象)。


我应该使用checkNotNull(myObject).getAnything();

不,你不应该。 这将是相当多余( 更新 )。

您应该使用checkNotNull为了快速失败 。 没有它,你可能会把一个非法的null传递给另一个方法,这个方法将会进一步传递,等等,最终会失败。 然后你可能需要一些好运来找出实际上第一个方法应该拒绝null


yshavit的回答提到了一个重要的问题:传递一个非法的价值是不好的,但是存储和传递它会更糟糕。

更新

其实,

  checkNotNull(myObject).getAnything() 

因为你明确表示你不接受任何空值的意图。 没有它,有人可能会认为你忘了支票,并将其转换成类似的东西

  myObject != null ? myObject.getAnything() : somethingElse 

OTOH,我不认为这个支票是值得的。 用更好的语言来说 ,types系统会考虑可空性,给我们一些语义糖

  myObject!!.getAnything() // checkNotNull myObject?.getAnything() // safe call else null myObject?.getAnything() ?: somethingElse // safe call else somethingElse 

对于可为空的myObject ,而标准点语法只有在已知myObject为非空时才被允许。

几分钟前我已经读完了这整个线程。 不过,我很困惑,为什么我们应该使用checkNotNull 。 然后看看番石榴的预处理类文件,我得到了我的预期。 过度使用checkNotNull会降低性能。

我的思想是checkNotNull方法是值得的数据validation来自用户直接或非常端API的用户交互。 它不应该在内部API的每个方法中使用,因为使用它你不能停止exception,而是纠正你的内部API,以避免exception。

根据DOC: 链接

使用checkNotNull:

 public static double sqrt(double value) { Preconditions.checkArgument(value >= 0.0, "negative value: %s", value); // calculate the square root } 

关于性能的警告

这个类的目的是为了提高代码的可读性,但是在某些情况下,这可能会带来很大的性能成本。 请记住,消息构造的参数值必须全部急切计算,即使先决条件检查成功(因为它几乎总是在生产中),自动装箱和可变参数数组的创build也可能发生。 在某些情况下,这些浪费的CPU周期和分配可能会增加一个真正的问题。 性能敏感的前提条件检查总是可以转换成习惯的forms:

 if (value < 0.0) { throw new IllegalArgumentException("negative value: " + value); }