1

The following compiles:

System.out.println("true" + null);

which I assume is because the existence of a string, converts "null" to a string as well.

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);

However, it doesn't compile.

Can anyone explain?

abc32112
  • 2,457
  • 9
  • 37
  • 53

6 Answers6

2

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

Community
  • 1
  • 1
Luiggi Mendoza
  • 85,076
  • 16
  • 154
  • 332
  • I appreciate your effort, but to explain why null prints as "null", why post bytecode when it is written in the docs. – PeterMmm Feb 05 '14 at 16:16
  • @PeterMmm I was just curious how this is done *behind the scenes* because the compiler is responsible for this to happen, but as we know, this isn't done magically. – Luiggi Mendoza Feb 05 '14 at 16:18
1

It doesn't compile because the statement operands true and null are not applicable for the + operation. The applicable couples are:

  • all numeric types, including wrappers. For example: new Integer(1) + new Integer(2)
  • String + Object (because the .toString() method is defined for every object). For example: "newObject().toString() + "cde".
  • String + null. For example: "abc" + null

Notes (thanks to @Luiggi and @Sotirios):

  • The result String.valueOf(null) won't work as a possible operand as it will throw a NullPointerException ...
  • ...but String.valueOf((Object) null) will return the String value "null"
Konstantin Yovkov
  • 62,134
  • 8
  • 100
  • 147
1
since apparently "true" functions as a string.

This assumption is wrong. It works because you call

public void println(boolean x)

However,

true + null

is not a valid statement in Java, as you cannot add nor append boolean and Object.

BobTheBuilder
  • 18,858
  • 6
  • 40
  • 61
1

From the JLS

If the type of either operand of a + operator is String, then the operation is string concatenation.

and concatenation is made by StringBuilder.append().

PeterMmm
  • 24,152
  • 13
  • 73
  • 111
0

The statement between the parens are executed before it is ever printed. The problem is not string conversion, the problem is, as others have stated, is that

true + null

is an addition problem that makes no sense.

"true" + null

however, is not addition--it's string concatenation. Since "true" is a string, the meaning of the + is changed from "addition" to "concatenation".

aliteralmind
  • 19,847
  • 17
  • 77
  • 108
0

In case of

System.out.println(true + null);

it tries to convert null to boolean and because boolean has only false and true values it can not cast and doesn't compile.

In case of

System.out.println("true" + null);

null casted to String because String can has null value (String is a Object)

Ashot Karakhanyan
  • 2,804
  • 3
  • 23
  • 28
  • "it tries to convert null to boolean and because boolean has only false and true values it can not cast and doesn't compile." - and if it did convert null to boolean, what would the `+` do? `true + false` doesn't make any sense. – Steve Kuo Feb 05 '14 at 15:51