SSerguey Asael Shinder
Java coding notes: the JVM, and writing software that lasts

Serguey Asael Shinder: String.valueOf(null) picks the char[] overload and throws instead of returning null

· by Serguey Asael Shinder / Serguey Shinder

String.valueOf is the method people reach for when they want a string and do not want to think about null. The javadoc for the Object overload promises exactly that: "Returns: if the argument is null, then a string equal to "null"; otherwise, the value of obj.toString() is returned." (https://docs.oracle.com/en/java/javase/25/docs/api/java.base/java/lang/String.html#valueOf(java.lang.Object))

So this looks safe:

String s = String.valueOf(null);

It compiles, and it throws NullPointerException. The overload that runs is not valueOf(Object).

String has a valueOf(char[] data) as well, and a bare null literal fits both. The Java Language Specification settles which one is called in §15.12.2.5, "Choosing the Most Specific Method": "The Java programming language uses the rule that the most specific method is chosen. The informal intuition is that one method is more specific than another if any invocation handled by the first method could be passed on to the other one without a compile-time error." (https://docs.oracle.com/javase/specs/jls/se25/html/jls-15.html#jls-15.12.2.5) Every char[] is an Object, not the other way round, so valueOf(char[]) is more specific and wins.

The char[] overload's own javadoc says nothing about null. The rule that applies is the class-level one: "Unless otherwise noted, passing a null argument to a constructor or method in this class will cause a NullPointerException to be thrown." (https://docs.oracle.com/en/java/javase/25/docs/api/java.base/java/lang/String.html) Only valueOf(Object) notes otherwise.

Serguey Asael Shinder: String.valueOf(null) picks the char[] overload and throws instead of returning null
String.valueOf(null) picks the char[] overload and throws instead of returning null — Serguey Asael Shinder

In real code nobody writes a literal null, which is why this survives review. What people write is a variable, and then the declared type decides:

Object o = null;
String.valueOf(o);        // "null"  -> valueOf(Object)
char[] chars = null;
String.valueOf(chars);    // NullPointerException -> valueOf(char[])

The dangerous case is a helper whose parameter type changes. A method that took Object value and passed it on to String.valueOf(value) behaves one way; refactor the parameter to char[] because the callers happen to pass passwords or buffers, and the same line now throws on null. The call site did not change, so nothing in the diff points at it.

Two habits avoid it. When null is a real possibility, say so instead of relying on overload resolution: Objects.toString(value, "null") or an explicit check. And when the argument is a char[] that may be absent, treat absence before converting, because valueOf(char[]) was never documented to accept it.

I have not run these snippets on a JDK on this machine; the behaviour above is what the cited JLS section and the javadoc of Java SE 25 specify.