简体   繁体   English

什么是番石榴checkNotNull的重点

[英]What's the point of Guava checkNotNull

I'm pretty new to Guava (let's be honest, I'm not "pretty new", I'm a complete rookie on that subject) and so I decided to go through some documentation and got quite amazed while reading this: 我是Guava的新手(说实话,我不是“非常新的”,我是这个主题的新手)所以我决定阅读一些文档并在阅读时非常惊讶:

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

I don't get the point of this method. 我不明白这个方法的意义。 This means that instead of doing : 这意味着,而不是做:

myObject.getAnything();

(which might cause a NullPointerException if myObject is null) (如果myObject为null,则可能导致NullPointerException

I should use 我应该用

checkNotNull(myObject).getAnything();

which will throw a NullPointerException if myObject is null and return myObject if it is not null. 如果myObject为null, 抛出NullPointerException如果不为null,则返回myObject

I'm puzzled and this might be the stupidest question ever but ... 我很困惑,这可能是有史以来最愚蠢的问题,但......

What is the point of this? 这有什么意义? Those two lines do the exact same thing as for outcomes given any situations I can think of. 考虑到我能想到的任何情况,这两行与结果完全相同。

I don't even think that the latter is more readable. 我甚至认为后者更具可读性。

So I must be missing something. 所以我一定错过了什么。 What is it? 它是什么?

The idea is to fail fast. 想法是快速失败。 For instance, consider this silly class: 例如,考虑这个愚蠢的类:

public class Foo {
    private final String s;

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

    public int getStringLength() {
        return s.length();
    }
}

Let's say you don't want to allow null values for s . 假设你不想允许s空值。 (or else getStringLength will throw a NPE). (否则getStringLength会抛出一个NPE)。 With the class as-is, by the time you catch that null , it's too late -- it's very hard to find out who put it there. 随着课程的原样,当你抓住那个null ,已经太晚了 - 很难找到谁把它放在那里。 The culprit could well be in a totally different class, and that Foo instance could have been constructed a long time ago. 罪魁祸首很可能是一个完全不同的类,并且Foo实例可能很久以前就构建过了。 Now you have to comb over your code base to find out who could possibly have put a null value there. 现在你必须梳理你的代码库,找出谁可能在那里放置一个null值。

Instead, imagine this constructor: 相反,想象一下这个构造函数:

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

Now, if someone puts a null in there, you'll find out right away -- and you'll have the stack trace pointing you exactly to the call that went wrong. 现在,如果有人把一个null在那里,你会发现马上 -你就会有堆栈跟踪指向正是你走错了电话。


Another time this can be useful is if you want to check the arguments before you take actions that can modify state. 另一个有用的方法是,如果要在执行可以修改状态的操作之前检查参数。 For instance, consider this class that computes the average of all string lengths it gets: 例如,考虑这个类计算它获得的所有字符串长度的平均值:

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;
    }
}

Calling accept(null) will cause an NPE to get thrown -- but not before stringsSeen has been incremented. 调用accept(null)将导致NPE被抛出 - 但是在stringsSeen增加之前stringsSeen This may not be what you want; 这可能不是你想要的; as a user of the class, I may expect that if it doesn't accept nulls, then its state should be unchanged if you pass a null (in other words: the call should fail, but it shouldn't invalidate the object). 作为类的用户,我可能期望如果它不接受空值,那么如果传递null,则其状态应该保持不变(换句话说:调用应该失败,但它不应该使对象无效)。 Obviously, in this example you could also fix it by getting s.length() before incrementing stringsSeen , but you can see how for a longer and more involved method, it might be useful to first check that all of your arguments are valid, and only then modify state: 显然,在这个例子中,您还可以通过在递增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(); (which might cause a NullPointerException if myObject is null) (如果myObject为null,则可能导致NullPointerException)

No... it will throw NPE whenever myObject == null . 不...只要myObject == null就会抛出NPE。 In Java, there's no chance of calling a method with null receiver (a theoretical exception are static methods, but they can and should be always called without any object). 在Java中,没有机会使用null接收器调用方法(理论上的异常是静态方法,但它们可以并且应该始终在没有任何对象的情况下调用)。


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

No you should not. 不,你不应该。 This would be rather redundant ( Update ). 这将是相当多余的( 更新 )。

You should use checkNotNull in order to fail fast . 你应该使用checkNotNull快速失败 Without it, you may pass an illegal null to another method, which passes it further, and so on and so on, where it finally fails. 没有它,您可以将非法null传递给另一个方法,该方法将其进一步传递,依此类推,等等,最终失败。 Then you can need some good luck to find out that actually the very first method should have refused null . 然后你可能需要一些好运才能发现实际上第一种方法应该拒绝为null


The answer by yshavit mentions an important point: Passing an illegal value is bad, but storing it and passing it later is even worse. yshavit的答案提到了一个重要的观点:传递非法价值是不好的,但是存储它并在以后传递它会更糟。

Update 更新

Actually, 其实,

 checkNotNull(myObject).getAnything()

makes sense, too, as you clearly express your intent to not accept any nulls. 也是有道理的,因为你明确表达了不接受任何空值的意图。 Without it, someone could think that you forgot the check and convert it into something like 没有它,有人可能会认为你忘了支票并把它转换成类似的东西

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

OTOH, I don't think the check is worth the verbosity. OTOH,我认为检查不值得冗长。 In a better language , the type system would consider nullability and give us some semantic sugar like 在一个更好的语言中 ,类型系统会考虑可空性并给我们一些语义糖

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

for nullable myObject , while the standard dot syntax would be allowed only when myObject is known to be non-null. 对于可为空的myObject ,只有当myObject被称为非null时才允许使用标准点语法。

I have read this whole thread few minutes ago. 几分钟前我已经阅读了整个帖子。 Nevertheless I was confused why should we use checkNotNull . 不过我很困惑为什么要使用checkNotNull Then look over Precondition class doc of Guava and I got what I expected. 然后查看Guava的Precondition类doc,我得到了我的预期。 Excessive use of checkNotNull will degrade performance definitely. 过度使用checkNotNull肯定会降低性能。

My thought is checkNotNull method is worth required for data validation which comes from user directly or very end API to user interaction. 我的想法是checkNotNull方法值得进行数据验证,它来自用户直接或非常终端API到用户交互。 It shouldn't be used in each and every methods of internal API because using it you can't stop exception rather correct your internal API to avoid Exception. 它不应该用在内部API的每个方法中,因为使用它不能停止异常而是纠正内部API以避免异常。

According to DOC: Link 根据DOC: Link

Using checkNotNull: 使用checkNotNull:

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

Warning about performance 关于性能的警告

The goal of this class is to improve readability of code, but in some circumstances this may come at a significant performance cost. 此类的目标是提高代码的可读性,但在某些情况下,这可能会带来显着的性能损失。 Remember that parameter values for message construction must all be computed eagerly, and autoboxing and varargs array creation may happen as well, even when the precondition check then succeeds (as it should almost always do in production). 请记住,消息构造的参数值必须都是急切地计算,并且自动装箱和varargs数组创建也可能发生,即使前置条件检查成功(因为它几乎总是在生产中)。 In some circumstances these wasted CPU cycles and allocations can add up to a real problem. 在某些情况下,这些浪费的CPU周期和分配可能会导致真正的问题。 Performance-sensitive precondition checks can always be converted to the customary form: 性能敏感的前置条件检查始终可以转换为常规形式:

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

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

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