Leo's Technical Blog

Null Evilness is a Java fault

Introduction

user

Leo Soto


advising, java, scala

Null Evilness is a Java fault

Posted by Leo Soto on .
Featured

advising, java, scala

Null Evilness is a Java fault

Posted by Leo Soto on .

This week Marty touched a sensible spot in his article "Returning None is Evil", where, aside from what's obvious from the title, he claims that this behaviour is something popular on the Java mindset.

As many people point out, it doesn't seem like a fault of Java, as nothing is really different here between Java and Python (both have exceptions, both have null|None).

But I think it is a fault of Java. Java is statically typed, Python isn't. If the compiler makes me type a bunch of information about the possible values of the data of my program (in fact, more than what's really needed), I want something back. Something like checking that I will never get an unexpected value into one of my variables.

The problem

Java, by allowing null values everywhere (except on primitive data types), doesn't fulfill my previous expectation. I must be always remembering that every method could return null. Same with every method argument. That's not acceptable when considering how much I'm paying for static typing.

Yeah, I know that there are cases where a "nothing here, move along" value is reasonable. But that doesn't explain why Java takes that to the extreme. Is it reasonable to have a null List? A null String? Nope, that's not reasonable and I'm wasting time when I'm forced to check for that.

Some solutions

So what would fix this? IMHO, every language that aspires to be a Good Statically Typed Language(TM) must have a way to differentiate between types that allow a special None|nil|null value and those that don't.

The easy way would be to annotate null or not null types, such as in:

public static void main(notnull String[] arg)  

At least then you could look on the signature of a method to know if it can return null. And unexpected null arguments wouldn't happen anymore. Nice takes this strategy, using a question mark to annotate nullable variables (?String foo = null is valid, but String foo = null isn't).

But the Really Good Way would be to use enumerate types and pattern matching as it's done on many functional languages. I'll show how that looks on Scala. There, Python's dict.get would be somewhat like:

def get<K,V>(key:K):Option[V] = ...  

So you can't just use the result of it without taking in consideration that it could be None (the previous solution doesn't necessarily force you to do so).

Therefore, you can pattern match it:

dict.get(key) match {  
  case Some(value) => [do something with value]
  None => [dome something else]
}

Or, when getting a Option[T] parameter, you can use a default value:

def greet(name:Option[String]) =  
    "hello " + name.getOrElse("anonymous")

Here I used the getOrElse method of Option, that saves me making a trivial (but somewhat verbose) pattern match.

The sad part is that in Scala, as in Java, every subclass of AnyRef (the equivalent of Java's Object) allows null value, so nothing prevents having an Option[String] variable pointing to null. It may be the price for being a Java-integrated language, but it's really annoying:

val foo:Option[String] = null  
foo match {case Some(s) => s case None => "none"}  
scala.MatchError: null  
        at .(:5)
        at .()
        at RequestResult$.(:3)
        at RequestResult$.()
        at RequestResult$result()
        at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
        at sun.reflect.NativeMethodAccessorImpl.invoke(Unknown Source)

Doh! Well, it's still better than Java, as hardly someone will object the summary execution of the programmer that assigned null to an Option (or to its value).

But Java won't change tomorrow!

Hey, this thing surely won't change ever. In the meantime our tools are assertions, the "NullObject pattern" and prominent documentation on methods that may return null.