188

Why does the following work? I would expect a NullPointerException to be thrown.

String s = null;
s = s + "hello";
System.out.println(s); // prints "nullhello"
yavoh
  • 2,645
  • 5
  • 24
  • 21
  • 4
    The *type* of s is known at compile time and the `+ operator` is overloaded upon the String type (see Jonathans answer, for instance). There are no method calls in the `s + "hello"` line and thus no chance for an NPE as there is no object receiver (and 'code transformations' must honor this contract). Happy coding. –  Nov 23 '10 at 21:46
  • 3
    I agree with your line of thinking yavoh. Automatically stringifying null is not a good part of Java. Shame on Sun for doing something so bug-prone. – B T Sep 29 '12 at 00:20
  • 1
    @user166390 Great explanation, isn't it, but printing `nullhello` still remains a counter-intuitive, useless behavior IMO. – aliopi Oct 08 '15 at 08:16
  • 1
    I think many developers have misunderstand that if I use a null object, it will crash. In fact, only when you call a null object's property or method, that will crash. – Spark.Bao Jul 29 '16 at 03:55
  • 1
    If the wise ones were to convert a null to an empty string "" i could somewhat understand, but convert to a literal 4-character sting "null"?! – vesperto Feb 06 '19 at 13:10
  • So, it's by definition of `+` operator when applied to `String` values. Eventually, it doesn't attempt to invoke a method on any of the operands unless it's of a different type, when an implicit `toString` shall be invoked. Clearly, the intention of `+` operator is to simplify value display, and not in any way refer to result length etc. – tishma Jun 18 '21 at 11:48

5 Answers5

204

Why must it work?

The JLS 5, Section 15.18.1.1 JLS 8 § 15.18.1 "String Concatenation Operator +", leading to JLS 8, § 5.1.11 "String Conversion", requires this operation to succeed without failure:

...Now only reference values need to be considered. If the reference is null, it is converted to the string "null" (four ASCII characters n, u, l, l). Otherwise, the conversion is performed as if by an invocation of the toString method of the referenced object with no arguments; but if the result of invoking the toString method is null, then the string "null" is used instead.

How does it work?

Let's look at the bytecode! The compiler takes your code:

String s = null;
s = s + "hello";
System.out.println(s); // prints "nullhello"

and compiles it into bytecode as if you had instead written this:

String s = null;
s = new StringBuilder(String.valueOf(s)).append("hello").toString();
System.out.println(s); // prints "nullhello"

(You can do so yourself by using javap -c)

The append methods of StringBuilder all handle null just fine. In this case because null is the first argument, String.valueOf() is invoked instead since StringBuilder does not have a constructor that takes any arbitrary reference type.

If you were to have done s = "hello" + s instead, the equivalent code would be:

s = new StringBuilder("hello").append(s).toString();

where in this case the append method takes the null and then delegates it to String.valueOf().

Note: String concatenation is actually one of the rare places where the compiler gets to decide which optimization(s) to perform. As such, the "exact equivalent" code may differ from compiler to compiler. This optimization is allowed by JLS, Section 15.18.1.2:

To increase the performance of repeated string concatenation, a Java compiler may use the StringBuffer class or a similar technique to reduce the number of intermediate String objects that are created by evaluation of an expression.

The compiler I used to determine the "equivalent code" above was Eclipse's compiler, ecj.

Andy Brown
  • 18,961
  • 3
  • 52
  • 62
Mark Peters
  • 80,126
  • 17
  • 159
  • 190
  • 3
    `StringBuilder` technically does not use `String.valueOf` if any `append` method other than `append(Object)` (such as `append(String)` or `append(CharSequence)`) is called, though `null` is changed to `"null"` regardless. – ColinD Nov 23 '10 at 20:54
  • Thanks @ColinD, in this case I was wrong since there is no contructor `StringBuilder(Object)`. I've edited my post to directly reflect the bytecode. – Mark Peters Nov 23 '10 at 21:04
  • More explanation for something which behaves unexpectedly wrong. It should print `hello`. – aliopi Oct 08 '15 at 08:19
  • Has this always been the case in Java? Or is this as-of some version? – DataDino Sep 22 '16 at 14:51
  • @aliopi I respectfully disagree. In my opinion, it should throw an NPE if the behaviour were to change at all, but I don't think that would really help. – Adowrath Jul 20 '17 at 13:30
  • 1
    @Adowrath I changed my mind, you are right, accessing `null` should throw an NPE. I think printing `null` is something like a _"meta"_-behaviour, for example when debugging, when I want to see what's inside that variable, but the default _"non-meta"_ behaviour should be throwing an NPE. – aliopi Jul 21 '17 at 10:13
  • 1
    @aliopi: I think they made a defensible decision here, making concatenation null-safe. It might violate the principle of least surprise, but they were probably trying to avoid a plethora of either NPEs or explicit null checks inside of logging and diagnostics code (e.g. `logger.print("doing something to " + obj);`) Nowadays we have better logging composition than string concatenation, but that wasn't necessarily true in 1995. – Mark Peters Jul 21 '17 at 13:56
  • `new StringBuilder("hello").append(s).toString()` is `hellonull` – Bejond Mar 09 '19 at 03:23
  • Note that ```s.concat("hello");``` would result in a NullPointereException. In Java, the behavior of the + operator is usually determined by the left operand: ```System.out.println(3 + 'a'); //100``` However, Strings are the exception. If either operand is a String, the result is expected to be a String. This is the reason null is converted into "null", even though you might expect a RuntimeException. – Christian Meyer Dec 30 '21 at 17:51
  • It makes little sense, intuitively, that String.valueOf(null) would return "null". Thrown exception, return null or imo best solution is to return empty string "". Oh well, we have to live with how it was implemented. Ideally we want variable s to be guaranteed never null, then s + "hello" would never be "nullhello". Also ideally, we want a more painful result to null + "hello" so that we catch our bug. Unit test can also catch it though, so ultimately the responsibility lies with us developers. – Skystrider Apr 28 '23 at 17:48
29

See section 5.4 and 15.18 of the Java Language specification:

String conversion applies only to the operands of the binary + operator when one of the arguments is a String. In this single special case, the other argument to the + is converted to a String, and a new String which is the concatenation of the two strings is the result of the +. String conversion is specified in detail within the description of the string concatenation + operator.

and

If only one operand expression is of type String, then string conversion is performed on the other operand to produce a string at run time. The result is a reference to a String object (newly created, unless the expression is a compile-time constant expression (§15.28))that is the concatenation of the two operand strings. The characters of the left-hand operand precede the characters of the right-hand operand in the newly created string. If an operand of type String is null, then the string "null" is used instead of that operand.

Jonathon Faust
  • 12,396
  • 4
  • 50
  • 63
  • 6
    Yes! Finally an answer with some nice quotes without jumping on 'StringBuilder' (which is an optimization the compiler may or may not perform). –  Nov 23 '10 at 21:45
14

The second line is transformed to the following code:

s = (new StringBuilder()).append((String)null).append("hello").toString();

The append methods can handle null arguments.

Roland Illig
  • 40,703
  • 10
  • 88
  • 121
12

You are not using the "null" and therefore you don't get the exception. If you want the NullPointer, just do

String s = null;
s = s.toString() + "hello";

And I think what you want to do is:

String s = "";
s = s + "hello";
krico
  • 5,723
  • 2
  • 25
  • 28
7

This is behavior specified in the Java API's String.valueOf(Object) method. When you do concatenation, valueOf is used to get the String representation. There is a special case if the Object is null, in which case the string "null" is used.

public static String valueOf(Object obj)

Returns the string representation of the Object argument.

Parameters: obj - an Object.

Returns:

if the argument is null, then a string equal to "null"; otherwise, the value of obj.toString() is returned.

wkl
  • 77,184
  • 16
  • 165
  • 176