The +
operator works for numbers (int
, float
, double
) and String
s, but in this case you're using it to add a boolean
and an Object
(which is null
), which is wrong.
However, since this works
System.out.println(true);
It would seem that the following would also compile, since apparently "true" functions as a string.
System.out.println(true + null);
The former works since System.out.println(boolean)
will convert the boolean
into a String
. The latter won't.
Doing some research about why null
converts to "null"
without generating any problem, since:
null.toString()
doesn't compile for obvious reasons
StringBuilder.append(null)
doesn't compile
String.valueOf(null)
compiles but throws a NullPointerException
at runtime
Looks like the compiler instead taking null
directly, it will first create an Object o
and then String s
variable and assign null
to it, then use String.valueOf(s)
. So, this code:
System.out.println("true" + null);
Is in fact
System.out.println(new StringBuilder().append("true").append((Object)null).toString());
If we do the inverse:
System.out.println(null + "true");
This will become:
System.out.println(new StringBuilder().append((Object)null).append("true").toString());
To show that this is nor magic nor black sorcery, I just wrote a simple code to replicate the situation:
public class TestNullString {
public static void main(String[] args) {
nullPlusString();
stringPlusNull();
}
public static void nullPlusString() {
System.out.println(null + "foo");
}
public static void stringPlusNull() {
System.out.println("foo" + null);
}
}
Compile the class using java TestNullString.java
, then decompiling it using javap -c TestNullString.class
, which generated (comments auto generated by decompiler itself, not mine):
Compiled from "TestNullString.java"
public class TestNullString {
public TestNullString();
Code:
0: aload_0
1: invokespecial #1 // Method java/lang/Object."<init>":()V
4: return
public static void main(java.lang.String[]);
Code:
0: invokestatic #2 // Method nullPlusString:()V
3: invokestatic #3 // Method stringPlusNull:()V
6: return
public static void nullPlusString();
Code:
0: getstatic #4 // Field java/lang/System.out:Ljava/io/PrintStream;
3: new #5 // class java/lang/StringBuilder
6: dup
7: invokespecial #6 // Method java/lang/StringBuilder."<init>":()V
10: aconst_null
11: invokevirtual #7 // Method java/lang/StringBuilder.append:(Ljava/lang/Object;)Ljava/lang/StringBuilder;
14: ldc #8 // String foo
16: invokevirtual #9 // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
19: invokevirtual #10 // Method java/lang/StringBuilder.toString:()Ljava/lang/String;
22: invokevirtual #11 // Method java/io/PrintStream.println:(Ljava/lang/String;)V
25: return
public static void stringPlusNull();
Code:
0: getstatic #4 // Field java/lang/System.out:Ljava/io/PrintStream;
3: new #5 // class java/lang/StringBuilder
6: dup
7: invokespecial #6 // Method java/lang/StringBuilder."<init>":()V
10: ldc #8 // String foo
12: invokevirtual #9 // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
15: aconst_null
16: invokevirtual #7 // Method java/lang/StringBuilder.append:(Ljava/lang/Object;)Ljava/lang/StringBuilder;
19: invokevirtual #10 // Method java/lang/StringBuilder.toString:()Ljava/lang/String;
22: invokevirtual #11 // Method java/io/PrintStream.println:(Ljava/lang/String;)V
25: return
}
By going to the implementation of StringBuilder#append(Object)
, it shows:
public StringBuilder append(Object obj) {
return append(String.valueOf(obj));
}
And String#valueOf(Object)
implementation:
public static String valueOf(Object obj) {
return (obj == null) ? "null" : obj.toString();
}
Which completely shows why null
becomes, in the end, "null"
.
Another effort to explain this was done here: Concatenating null strings in Java