It's not about null specifically, it's about the null expression's type.
A null expression doesn't have to be typed, but it can be. Here is a trivial example:
String x = null;
System.out.println(x);
This is very slightly different compared to:
System.out.println(null);
Note that println has a boatload of overloads - completely different methods (and javac, the compiler, must pick which exact overload to invoke at compile time), that are all named println, all of which have 1 parameter. The first snippet is easy for the compiler: You clearly mean to call that println that takes a single String as parameter. However, the second is ambiguous. If I attempt to compile the second snippet, the compiler will literally tell me so:
error: reference to println is ambiguous
This shows how, even though the runtime behaviour of this would seem to be 100% identical (both null and x are expressions whose value will definitely be null), turns out, at least at compile time, this is not true.
The same principle can explain what you see here, with your varargs, in a slightly different way. Given:
void validateLocationIds(String... locationIds) {
That's just syntax sugar. Specifically, it's syntax sugar for:
- The method's signature is
validateLocationIds(String[] locationIds).
- However, you are telling the compiler, during the compilation of calls to this method, to treat a sequence of arguments that can all be treated as
String arguments to go ahead and treat that as if the caller had written validateLocationIds(new String[] {a, b, c}) instead of what you typed (validateLocationIds(a, b, c);).
Crucially the compiler will only do that if it has to - if the code would not compile if it didn't do this 'fold the args into an array'.
This then gets us (presumably, there are some more exotic alternate explanations, this is merely the most likely) to an explanation. Given:
String x = null;
validateLocationIds(x);
There is only one way for the compiler to make this work: By treating x as a String and therefore syntax sugaring that into new String[] {x}, i.e. a new string array of size 1 whose first and only entry is null. Because otherwise it would not compile; a variable of type String simply isn't a String[]. Even if the variable's value is currently null which is also a valid value for String[] - because how could the compiler possibly know that for sure? It can't, so it won't just go: Oohhh,, I see, a variable of type String, but, I know its null so I can treat it as whatever the heck I want, really. It does not do that.
In contrast to spelling it out literally:
validateLocationIds(null);
This is not ambiguous, unlike our println example: The Java Language Spec, in the section on picking between multiple different overloads, explicitly states that the compiler must error out with a 'call is ambiguous' error, but, when it's about whether to apply varargs sugaring or not, that is not the case: The array "wins", so to speak. In other words, if the call could both be treated as a straight method call (the expression is the array), as well as sugaring into an array (you could zip that one arg into a 1-len array and pass that instead), then 'just treat as array' wins.
This makes sense. Imagine that would be an error condition. Then you could NEVER pass an object array to void foo(Object...). After all, an array -is also an object-, and could therefore be 'zipped'.
String var = null; validateLocationIds(var)and use Intellij's Evaluate Expression to call the code, it's treatinglocationIdsasnull. At least according to the error message