[英]What's the point of Guava checkNotNull
我是Guava的新手(说实话,我不是“非常新的”,我是这个主题的新手)所以我决定阅读一些文档并在阅读时非常惊讶:
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)。 随着课程的原样,当你抓住那个null
,已经太晚了 - 很难找到谁把它放在那里。 罪魁祸首很可能是一个完全不同的类,并且Foo
实例可能很久以前就构建过了。 现在你必须梳理你的代码库,找出谁可能在那里放置一个null
值。
相反,想象一下这个构造函数:
public Foo(String s) {
this.s = checkNotNull(s);
}
现在,如果有人把一个null
在那里,你会发现马上 -你就会有堆栈跟踪指向正是你走错了电话。
另一个有用的方法是,如果要在执行可以修改状态的操作之前检查参数。 例如,考虑这个类计算它获得的所有字符串长度的平均值:
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
增加之前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,我认为检查不值得冗长。 在一个更好的语言中 ,类型系统会考虑可空性并给我们一些语义糖
myObject!!.getAnything() // checkNotNull
myObject?.getAnything() // safe call else null
myObject?.getAnything() ?: somethingElse // safe call else somethingElse
对于可为空的myObject
,只有当myObject
被称为非null时才允许使用标准点语法。
几分钟前我已经阅读了整个帖子。 不过我很困惑为什么要使用checkNotNull
。 然后查看Guava的Precondition类doc,我得到了我的预期。 过度使用checkNotNull
肯定会降低性能。
我的想法是checkNotNull
方法值得进行数据验证,它来自用户直接或非常终端API到用户交互。 它不应该用在内部API的每个方法中,因为使用它不能停止异常而是纠正内部API以避免异常。
根据DOC: Link
使用checkNotNull:
public static double sqrt(double value) {
Preconditions.checkArgument(value >= 0.0, "negative value: %s", value);
// calculate the square root
}
关于性能的警告
此类的目标是提高代码的可读性,但在某些情况下,这可能会带来显着的性能损失。 请记住,消息构造的参数值必须都是急切地计算,并且自动装箱和varargs数组创建也可能发生,即使前置条件检查成功(因为它几乎总是在生产中)。 在某些情况下,这些浪费的CPU周期和分配可能会导致真正的问题。 性能敏感的前置条件检查始终可以转换为常规形式:
if (value < 0.0) {
throw new IllegalArgumentException("negative value: " + value);
}
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.