The + operator works for numbers (int, float, double) and Strings, 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