52

Take a look at the following example:

class nul
{
  public static void main (String[] args)
  {
    System.out.println (String.valueOf((Object)null));
    System.out.println (String.valueOf(null));
  }
}

The first println writes null but the second throws a NullPointerException.

Why is only the second line worth an exception? And what is the difference between the two nulls? Is there a real null and a fake null in Java?

Rohit Jain
  • 209,639
  • 45
  • 409
  • 525
ceving
  • 21,900
  • 13
  • 104
  • 178
  • 1
    If you look at the Javadoc for String the answer is staring at you in the two relevant overloads of valueOf. – bmargulies Oct 11 '13 at 11:36
  • highly related: http://stackoverflow.com/questions/3131865/why-does-string-valueofnull-throw-a-nullpointerexception?rq=1 – tckmn Oct 11 '13 at 23:11

3 Answers3

56

The first invocation will call the String.valueOf(Object) method, as you have explicitly typecasted null to Object reference. Conversely, the second one will invoke the overloaded String.valueOf(char[]) method, as char[] is more specific than Object for a null argument.

There are other overloaded versions of this method that accept primitive parameters, but those are not a valid match for a null argument.

From JLS §15.12.2:

There may be more than one such method, in which case the most specific one is chosen. The descriptor (signature plus return type) of the most specific method is one used at run time to perform the method dispatch.

A method is applicable if it is either applicable by subtyping (§15.12.2.2), applicable by method invocation conversion (§15.12.2.3), or it is an applicable variable arity method (§15.12.2.4).

[...]

If several applicable methods have been identified during one of the three phases of applicability testing, then the most specific one is chosen, as specified in section §15.12.2.5.

Now check the source code of both the methods:

// This won't throw NPE for `obj == null`
public static String valueOf(Object obj) {
    return (obj == null) ? "null" : obj.toString();
}

// This will throw `NPE` for `data == null`
public static String valueOf(char data[]) {
    return new String(data);
}
nbrooks
  • 18,126
  • 5
  • 54
  • 66
Rohit Jain
  • 209,639
  • 45
  • 409
  • 525
  • 6
    (Because the compiler resolves the methods called with `null` as a parameter as the most concrete as it can find. And `char[]` is more concrete than `Object`.) – Petr Janeček Oct 11 '13 at 11:34
  • 1
    So this is a Java bug, because the two implementations of `valueOf` do not do the same. The Object implementation thinks null should be printed as null, but the char array implementation thinks null should cause an exception. The same method thinks different about what is exceptional. – ceving Oct 11 '13 at 11:54
  • 5
    @ceving java bug? No, definitely not! The `Object` _should_ be printed as `null`. And a `null` `char[]` cannot be converted into a `String` so an exception is thrown. They are _different_ methods, that is the whole point. Where is the bug exactly? – Boris the Spider Oct 11 '13 at 12:11
  • Just a curiosity: when talking about methods, is it more standard to use the notation `Class#method(Type)` or `Class.method(Type)`? Is there a standard? –  Oct 11 '13 at 12:27
  • 1
    @htor `.` for `static` methods, and `#` for `instance` methods. Well, no there isn't any standard. – Rohit Jain Oct 11 '13 at 12:28
  • 2
    @BoristheSpider If they would have a different name, I would agree with you. But they have the same name and therefore are not different methods. – ceving Oct 11 '13 at 13:38
  • 1
    @ceving: they are two different overloads, so they simply aren't the same method. But I do see your point. – Blaisorblade Oct 11 '13 at 17:22
  • @BoristheSpider, I'd still argue this violates the principle of least surprise, because intuitively upcasting a value shouldn't fix a failure. This intuition is justified by Liskov substitution principle (LSP). If String.valueOf were a method of Object (which it can't in Java because Java isn't fully OO) and had this behavior, it would clearly violate LSP. – Blaisorblade Oct 11 '13 at 17:30
  • A stronger argument is that since (char[]) null == (Object) null, any function (like String.valueOf) should intuitively give the same result. Of course that cannot be guaranteed with overloading, so quite some respectable language designers argue that overloading is a feature a language should not have. Because of this, I'd argue a different specification for either `String.valueOf` overload, preventing this, would arguably have been better (you haven't defended the current spec). Of course there's no way to change the behavior now, so reporting it as a bug would have little effect. – Blaisorblade Oct 11 '13 at 17:35
16

There are lots of overloaded String.valueOf methods in Java. Further, in Java null has any and all types so that anything (that isn't a primitive) can be null.

So, when you call (String.valueOf((Object)null) you call the valueOf method that takes an Object as use explicitly cast null to Object.

In the second example you don't explicitly cast the null to any particular type so in fact you call the valueOf method with a char[] which throws an NPE.

From the JLS §15.12.2

The second step searches the type determined in the previous step for member methods. This step uses the name of the method and the types of the argument expressions to locate methods that are both accessible and applicable, that is, declarations that can be correctly invoked on the given arguments.

There may be more than one such method, in which case the most specific one is chosen. The descriptor (signature plus return type) of the most specific method is one used at run time to perform the method dispatch.

In this case char[] is more specific than Object so it is called when no explicit cast of null is made.

Boris the Spider
  • 59,842
  • 6
  • 106
  • 166
  • And hence the bottom line is avoid passing raw `null` as arguments to methods rather cast them to param type of that method. :) – sactiw Jun 19 '15 at 14:00
6

Although I accepted already an answer I would like to add the exact answer to the question, because the two answers concentrate on explaining the trap I walked into.

The difference between (Object)null and null is that the type of the first is forced to Object but the type of the second is not, as one could think, forced to Object. Instead it could also be an array instead of an object.

So the conclusion is: pass (Object)null instead of null as an argument to a method to be sure to get exactly the method working on objects instead of any other method working on arrays.

ceving
  • 21,900
  • 13
  • 104
  • 178