繁体   English   中英

什么是番石榴checkNotNull的重点

[英]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.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM