什么是番石榴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); }