Object a = null;
String s = "foo bar " + a; // s == "foo bar null"
So, it's possible to get "null" as a string downstream, when some variable that should be non-nulleable - was null. If you find such a bug and incompetently fix it by checking for "null" downstream instead of checking before turning variables into strings - you have the error from the article. Especially if you check ignoring the case.
I've known Java to be a bit verbose, but I thought it was mostly a reasonable language aside from that. I might have expected something like that from Javascript or PHP. Never in my wildest dreams would I have thought that boring old Java would interpret a null cast to a string as a literal string "null" when combining strings. Even Ruby and Python don't do that - they throw type mismatch errors instead. C# treats it as empty string.
Ehh, I don't really think this is a huge deal personally. Keep in mind that it is not that `(string)null` gives you the "null" string, or `((string)null).ToString()` gives you the null string. You have to use actual string concatenation to get it (Or as others have pointed out, `String.ToValue(null)`). Point being, it isn't/shouldn't be messing up your regular comparisons (Unless you turn it into a string beforehand), and you really shouldn't be checking for stuff like `null` against a string literal anyway - it's meant for displaying a human readable string, not an unambiguous string that could be converted back at a later time. If you wanted that then you should use proper serialization techniques rather then just concatenating a bunch of values.
It's not that it's really that huge of a deal, just that it's so out of character for Java and everything it is seen as.
It's like if a guy you were friends with since college and always knew to be solid and reliable but a little boring, and now many years later, he's happily married with kids, watching sports, working in a large hierarchical organization, doing pretty average stuff but nothing unusual or exciting. If you suddenly found out that guy had secretly been a Furry the whole time, that would be really shocking.
Not that there's anything wrong with being boring and reliable, or being a furry. But the sudden change in how you saw somebody or something is stunning.
I half expect anything written in Java to be littered with AbstractFactoryFactories and giant frameworks using 20 different design patterns to write Hello World. I never would have expected Java to silently convert an actual null to the string "null". I always thought the one thing you can count on Java for was to be strictly strongly typed, and never silently do any weird random conversions that nobody would have expected. Guess I was wrong.
Though poking around in a few other languages, JavaScript does indeed do that also, though I kinda expect JavaScript to do things like that. In Ruby, nil.to_s gives empty string, and adding a nil to a string gives you a type error. In Python, trying to add None to a string also gives you a type error, but str(None) does give you 'None'. That's a bit disappointing, but not shocking to me.
A lot of languages overload + to mean concatenate. I think that's a mistake, however...
In Python:
"abc" + None
TypeError: cannot concatenate 'str' and 'NoneType' objects
"abc".join(None)
TypeError: can only join an iterable
In Ruby
"abc" + nil
TypeError: no implicit conversion of nil into String
In Lua
= "abc" .. Nil
attempt to concatenate global 'Nil' (a nil value)
But not Java. Java implicitly tries to coerce the provided value to be a string, which seems out of place in a language that values type safety. That all types might also be null also seems out of place in a language with strong, static typing, but that's been discussed to death. The problem is compounded by the fact that null is actually converted to "null" instead of an empty string.
That's a NameError in Python: undefined variables don't have a value, not even None. It won't be caught until runtime since there's no AOT compiler in Python, but it's the same kind of error it is in Java.
My claim isn't that Python is safer overall than Java. Instead, it's that Java, a language that is mostly type safe, most of the time should not have these two potentially surprising behaviors:
1. The standard string concatenation operator does implicit coercion rather than rejecting an input that isn't a string. There should be a builtin to make a string from any value no matter what for logging and debugging, but that shouldn't be the standard concatenation operator.
2. This is more controversial, but strongly statically typed languages should not allow arbitrary values to be null. That sabotages one of the major strengths of strong static typing. Instead, there should be an option type to make it explicit. For something familiar to most programmers, SQL does this.
The reason is that the string concatenation operation is doing a String.valueOf() on the argument, and that delegates to toString() that promises a human readable string that is relevant.
For some reason someone decided to check for null instead of the stricter option of throwing a NullPointerException. Maybe to help debugging or to follow the gist of toString().
I guess it was too late to change even in Java 0.9... Autoboxing, iterators and other newer features are more strict,so I suppose it's just one of the few irregularities left from prehistoric times...
> it's possible to get "null" as a string downstream
I don't see the problem with that. Strings that contain formatted variables should be used for display only. Besides, when someone inputs his name, the input is already a string, and I don't see how you would dereference string contents.
Sure – that's just casting a null type as a string.
So I guess if you have a comparison that somehow casts null to a string before comparing, you could run into this issue, but that's still bad programming.
I used to own null@myundergrad.edu as an email alias (with my real university, not that generic .edu, of course) and I got all sorts of interesting things... but that was very intentional on my part.
Funnily enough - it's not the null-> String conversion, it's the "+" operator. That was at one point subject of heated debate in my previous job :)
See:
"null".equals((String)null) //false
"null".equals((String)null+"") //true
System.out.print((String)null) // throws NPE
System.out.println((String)null) // prints "null", I guess because it appends "\n" inside
Right, but that operator is implicitly casting the null object to a string type – it has to, in a strict sense... some other languages would raise an error (and many would do the same as Java).
It doesn't have to. null.toString() throws NPE as it should. I would expect "foo" + null to throw NPE as well.
BTW there's another "fun" gotcha, when you interface java code and oracle database which consider empty string and null to be the same thing. Depending on how you handle data from database you end up with null, "", or "null" :)
String.valueOf(null) returns "null" though, and that is what "foo" + null uses. I can understand why it would be surprising if you thought it used .toString, but it's clearly listed in the standard.
BTW guess how I know it's working like that :)