This behavior is a species of type coercion.
In Apex, as in many other languages, when you apply an operator to operands of mixed type, the value of one of the operands is coerced to the type of the first to make the operation meaningful. For example,
Date myDate = Date.today();
String hello = 'Hello on ';
System.debug(hello + myDate);
will print out a message with today's date (n.b. in a date locale format that is often not very useful!) because the
Date value is coerced to a
This behavior applies to a variety of types to make concatenating them with strings (
+) meaningful, and it means you don't have to explicitly call
String.valueOf() when you do so.
So I think that's part of what's going on in your question. When we do
System.debug(a + b); // output nullnull
b is being coerced to a string without regard to its (
null) value, yielding
That's what the docs mean when they say
If x is a String and y is a String or any other type of non-null argument, concatenates y to the end of x.
The second clause is stating that type coercion will be performed.