4

Does anybody know why String.compareTo is not programmed to behave more graciously in regards to a null parameter?

In my opinion the next sequence should return "1" or at least the javadoc should be more specific in regards to the NPE. equals() returns false in this case and I guess equals and compareTo should be consistent.

E.g.

String nullString = null;
System.out.println ("test".equals(nullString));
System.out.println ("test".compareTo(nullString));

Is throwing a NPE:

false
Exception in thread "main" java.lang.NullPointerException
    at java.lang.String.compareTo(String.java:1177)

To add to it. If you were to make this compareTo in your code, would have you verified for nulls?

mihaisimi
  • 1,911
  • 13
  • 15
  • 5
    I think the javadoc is quite specific. – Woot4Moo Jun 26 '12 at 12:56
  • 1
    I guess there is not an universal answer, even you started with "in my opinion" so any decision is an arbitrary choice. Anyway I admit I agree with you, actual rule isn't very intuitive and it's not symmetric with equals but...it's by design... – Adriano Repetti Jun 26 '12 at 12:57
  • Why should it return 1 and not -1 ? It doesn't make sense to define an ordering with nulls. – Dorian Gray Sep 12 '17 at 16:51

3 Answers3

11

Because it's the documented behavior of compareTo() (emphasis added):

The natural ordering for a class C is said to be consistent with equals if and only if e1.compareTo(e2) == 0 has the same boolean value as e1.equals(e2) for every e1 and e2 of class C. Note that null is not an instance of any class, and e.compareTo(null) should throw a NullPointerException even though e.equals(null) returns false.

As for .equals() returning false instead of NPEing, again, it's in the documentation:

For any non-null reference value x, x.equals(null) should return false.

Matt Ball
  • 354,903
  • 100
  • 647
  • 710
  • In this case equals should have thrown NPE as well. – mihaisimi Jun 26 '12 at 12:58
  • 1
    @mihaisimi no it should not have, the contract of the interface explicitly states that e.equals(null) will yield false. This is an important thing to recognize when implementing interfaces and using code written by others. – Woot4Moo Jun 26 '12 at 13:01
  • I think "null is not an instance of any class" is pretty weak. I understand it's by design but it's not consistent with equals(). Even if equals() accepts an object the equalsIgnoreCase() method is consistent (and it accepts a string). – Adriano Repetti Jun 26 '12 at 13:03
  • 1
    @Adriano you don't have to make your case with me: that quote is straight from the JavaDoc. I agree, it's not completely consistent. – Matt Ball Jun 26 '12 at 13:04
  • @Adriano I am curious what would you consider null to be than? – Woot4Moo Jun 26 '12 at 13:07
  • @MattBall I know and you had my +1, I commented here only 'cause you quoted javadoc! – Adriano Repetti Jun 26 '12 at 13:07
  • @Woot4Moo I understand equals(null) returns false (null is not a string). I understand equalsIgnoreCase(null) because it must be consistent with equals(). I would expect compareTo(null) is consistent with equals (even if -1 or +1 for the result is pretty arbitrary). – Adriano Repetti Jun 26 '12 at 13:09
  • @Adriano I agree that far as well. The only thing I can think is that they were designing it similar to how NaN works when you do comparisons. – Woot4Moo Jun 26 '12 at 13:12
  • @Adriano the problem with `foo.compareTo(null)` always returning the same value is that `null` does not provide enough information to give a consistent ordering, particularly when the arguments are swapped: `null.compareTo(foo)` should _definitely_ NPE. I actually think this is a Good Thing, because the language is not imposing an arbitrary ordering of nulls (should they come before all other values, or after all other values?). In this regard, it is up to the programmer to decide. (continued) – Matt Ball Jun 26 '12 at 13:32
  • @Adriano ...which is where Guava's [`Ordering.nullsFirst|Last()`](http://docs.guava-libraries.googlecode.com/git/javadoc/com/google/common/collect/Ordering.html#nullsFirst%28%29) comes in handy. – Matt Ball Jun 26 '12 at 13:32
  • @MattBall of course there are good reasons for any design choice they made (= they're not random) but I'm pretty annoyed to treat null as a special case with an inconsistent behavior (null.equals(foo) throws NPE and it's OK, foo.equals(null) doesn't, foo.CompareTo(null) does). They loved to write documentation... – Adriano Repetti Jun 26 '12 at 13:42
3

From the javadoc

Compares this String to another Object. If the Object is a String, this function behaves like compareTo(String). Otherwise, it throws a ClassCastException (as Strings are comparable only to other Strings).

The only other option would be to throw a ClassCastException however, since there is a well defined exception for null references, the NPE is thrown instead.

Executing the following code:

void do()
{
    String s = null;
    System.out.println(s instanceof String); 
}

yields false.

Woot4Moo
  • 23,987
  • 16
  • 94
  • 151
  • It would call 'int compareTo(String anotherString)' though, ClassCastException is not applicable here – jontro Jun 26 '12 at 12:58
  • @jontro no it would not, see the updated code sample to validate the claim. In the above a String that is set to null is no longer a String. – Woot4Moo Jun 26 '12 at 12:59
  • You are right (by Java dogma). But I think the JDK generates bugs here and should be simplified. – mihaisimi Jun 26 '12 at 13:03
  • @mihaisimi you can email James Gosling if you would like. – Woot4Moo Jun 26 '12 at 13:04
  • @Woot4Moo I don't think the ClassCastException argument is the right one in this case - MattBall's answer is the correct answer. – assylias Jun 26 '12 at 13:20
  • 1
    @Woot4Moo Overloading is compile time. instanceof is runtime. compareTo(String) will be chosen. – jontro Jun 26 '12 at 13:22
  • cf JLS: "The null reference can always undergo a widening reference conversion to any reference type." – assylias Jun 26 '12 at 13:28
1

It is generally understood that the Java SE class library documentation explicitly states when null is a valid parameter. When there is no mention of null being a valid argument, you should assume that it is not, and that an exception may be thrown immediately, or later one.

And in this case, the javadoc for the Comparable interface specifically states that null is NOT a valid parameter for Comparable,compareTo. (You can get there from the String.compareTo javadoc by clicking the "Specified by" link.)

Stephen C
  • 698,415
  • 94
  • 811
  • 1,216