Is it a bad idea if equals(null) throws NullPointerException instead?

前端 未结 12 652
借酒劲吻你
借酒劲吻你 2020-12-02 07:00

The contract of equals with regards to null, is as follows:

For any non-null reference value x, x.equals(null) sho

相关标签:
12条回答
  • 2020-12-02 07:20

    If you take object oriented concepts into account, and consider the whole sender and receiver roles, I'd say that behaviour is convenient. See in the first case you're asking an object if he is equal to nobody. He SHOULD say "NO, I'm not".

    In the second case though, you don't have a reference to anyone So you aren't really asking anyone. THIS should throw an exception, the first case shouldn't.

    I think it's only asymmetric if you kind of forget about object orientation and treat the expression as a mathematical equality. However, in this paradigm both ends play different roles, so it is to be expected that order matters.

    As one final point. A null pointer exception should be raised when there's an error in your code. However, Asking an object if he is nobody, shouldn't be considered a programming flaw. I think it's perfectly ok to ask an object if he isn't null. What if you don't control the source that provides you with the object? and this source sends you null. Would you check if the object is null and only afterwards see if they are equals? Wouldn't it be more intuitive to just compare the two and whatever the second object is the comparison will be carried out without exceptions?

    In all honesty, I would be pissed if an equals method within its body returns a null pointer exception on purpose. Equals is meant to be used against any sort of object, so it shouldn't be so picky on what it receives. If an equals method returned npe, the last thing on my mind would be that it did that on purpose. Specially considering it's an unchecked exception. IF you did raise an npe a guy would have to remember to always check for null before calling your method, or even worse, surround the call to equals in a try/catch block (God I hate try/catch blocks) But oh well...

    0 讨论(0)
  • 2020-12-02 07:21

    Not that this is neccessarily an answer to your question, it is just an example of when I find it useful that the behaviour is how it is now.

    private static final String CONSTANT_STRING = "Some value";
    String text = getText();  // Whatever getText() might be, possibly returning null.
    

    As it stands I can do.

    if (CONSTANT_STRING.equals(text)) {
        // do something.
    }
    

    And I have no chance of getting a NullPointerException. If it were changed as you suggested, I would be back to having to do:

    if (text != null && text.equals(CONSTANT_STRING)) {
        // do something.
    }
    

    Is this a good enough reason for the behaviour to be as it is?? I don't know, but it is a useful side-effect.

    0 讨论(0)
  • 2020-12-02 07:23

    This is a tricky question. For backward compatability you can't do so.

    Imagine the following scenario

    void m (Object o) {
     if (one.equals (o)) {}
     else if (two.equals (o)) {}
     else {}
    }
    

    Now with equals returning false else clause will get executed, but not when throwing an exception.

    Also null is not really equal to say "2" so it makes perfect sense to return false. Then it is probably better to insist null.equals("b") to return also false :))

    But this requirement does make a strange and non symmetric equals relation.

    0 讨论(0)
  • 2020-12-02 07:24

    Think of how .equals is related to == and .compareTo is related to the comparison operators >, <, >=, <=.

    If you're going to argue that using .equals to compare an object to null should throw a NPE, then you'd have to say that this code should throw one as well:

    Object o1 = new Object();
    Object o2 = null;
    boolean b = (o1 == o2); // should throw NPE here!
    

    The difference between o1.equals(o2) and o2.equals(o1) is that in the first case you're comparing something to null, similar to o1 == o2, while in the second case, the equals method is never actually executed so there's no comparison happening at all.

    Regarding the .compareTo contract, comparing a non-null object with a null object is like trying do this:

    int j = 0;
    if(j > null) { 
       ... 
    }
    

    Obviously this won't compile. You can use auto-unboxing to make it compile, but you get a NPE when you do the comparison, which is consistent with the .compareTo contract:

    Integer i = null;
    int j = 0;
    if(j > i) { // NPE
       ... 
    }
    
    0 讨论(0)
  • 2020-12-02 07:24

    There are many common situations where null is not in any way exceptional, e.g. it may simply represent the (non-exceptional) case where a key has no value, or otherwise stand for “nothing”. Hence, doing x.equals(y) with an unknown y is also quite common, and having to always check for null first would be just wasted effort.

    As for why null.equals(y) is different, it is a programming error to call any instance method on a null reference in Java, and therefore worthy of an exception. The ordering of x and y in x.equals(y) should be chosen such that x is known to not be null. I would argue that in almost all cases this reordering can be done based on what is known about the objects beforehand (e.g., from their origin, or by checking against null for other method calls).

    Meanwhile if both objects are of unknown “nullness”, then other code almost certainly requires checking at least one of them, or not much can be done with the object without risking the NullPointerException.

    And since this is the way it is specified, it is a programming error to break the contract and raise an exception for a null argument to equals. And if you consider the alternative of requiring an exception to be thrown, then every implementation of equals would have to make a special case of it, and every call to equals with any potentially null object would have to check before calling.

    It could have been specified differently (i.e., the precondition of equals would require the argument to be non-null), so this is not to say that your argumentation is invalid, but the current specification makes for a simpler and more practical programming language.

    0 讨论(0)
  • 2020-12-02 07:26

    I think it's about convenience and more importantly consistency - allowing nulls to be part of the comparison avoids having to do a null check and implement the semantics of that each time equals is called. null references are legal in many collection types, so it makes sense they can appear as the right side of the comparison.

    Using instance methods for equality, comparison etc., necessarily makes the arrangement asymmetric - a little hassle for the huge gain of polymorphism. When I don't need polymorphism, I sometimes create a symmetric static method with two arguments, MyObject.equals(MyObjecta, MyObject b). This method then checks whether one or both arguments are null references. If I specifically want to exclude null references, then I create an additional method e.g. equalsStrict() or similar, that does a null check before delegating to the other method.

    0 讨论(0)
提交回复
热议问题