Everyone knows that
java.lang.Object is the common superclass of all Java classes. It is also the common supertype of all interfaces, which do not 'extend'
Object but do support the
Object protocol. This makes it the Top type, useful for programming generic algorithms.
Top represents all values in a programming language. It ensures that the type hierarchy is a complete partial order by providing an upper bound for every pair of types. Computing the upper bound of types is what makes assignment and method call work (via widening reference conversion), so a well-founded type hierarchy is important.
(Ignore that the complete partial order for primitive types is distinct from the complete partial order for reference types. Sigh.)
The counterpart to Top is Bottom, a type that is the common subtype of all other types. Bottom makes the type hierarchy into a lattice because it ensures every pair of types has a lower bound. Lower bounds play a role in Java wildcards - specifically, capture conversion and type inference - so it could be useful to know that every type has a lower bound.
Java has the null type. Pre-JLS3, the null type was not officially the subtype of any type, and the null reference was not officially a value of any type except the null type. A decree made the null reference castable to any reference type for pragmatic reasons. (This is similar to the decree that makes
List<?> assignable to a
List<T> formal parameter even though
List<?> is not a subtype of
List<T>. You know the decree as capture conversion.) JLS3 defines the null type as a subtype of every type, so it looks an awful lot like Bottom.
(Strictly, JLS3 restricts the null type to be a subtype of every reference type. Again, just ignore primitive types.)
The null type is expressible, i.e. can be the type of a term. The compiler will expose it if necessary, e.g.
int x = true?null:null;. But it is not denotable, i.e. cannot be written as the type of a term. You can't write
NullType v = null;. An RFE asks for a name for the null type. Is this a good idea?
Beyond the use case in the RFE, being able to denote NullType would be useful in certain situations where type inference fails, because NullType may be a better actual type argument than Object. So that's in NullType's favor.
Bottom is usually not a denotable (or even expressible) type in textbook type systems because type rules must be special-cased to ignore it. (See Pierce 15.4,16.4) But in Java, the presence of a value for the null type means expression evaluations has always had to consider the null type, responding with a
NullPointerException. (Indeed, the null reference means that the null type is not a true Bottom type.) Introducing NullType would allow more variables to store the null reference, but such variables evaluate to the null reference just like any variable of reference type can.
Statements would need tweaking. Consider the
if statement: "The Expression must have type boolean or Boolean, or a compile-time error occurs." A type system with Bottom would allow the expression to have the Bottom type by subsumption, so traditionally an extra rule would catch that case and assign Bottom as the type of the whole statement. We just want
if ([expression of null type]) ... to be illegal, so would need an "exactly" before "type boolean or Boolean".
[The first version of this blog entry said this wasn't necessary because final types didn't have any subtypes, not even the null type. Prompted by Remi's comment, I changed my mind. While a final class has no further implementations, special subtypes are possible.]
So, since Java already has the null reference, there is less problem adding NullType than if the null reference didn't exist.
Arrays cause a slight pain. A NullType can store only null values, which appears useless but someone will want it. On the face of it, we need the null type to be reified to enforce array covariance:
NullType n = new NullType;
Object o = n;
o = new Object(); // Statically safe, dynamically unsafe - Object, a supertype of NullType, is now stored in n
To avoid reification, we could define NullType as a static equivalent of
List<? super NullType>. Then, elements could be added to a NullType but not removed (except as
Object). A more drastic idea is to make arrays of NullType denotable but uninstantiatable, like arrays of generic types. The value of all this is becoming questionable.
Denoting the null type is less useful in Java than might be expected. Consider the classic uses of the Bottom type:
- A return type for a function which doesn't return. Since the Bottom type is empty, the function has no possible return value. Those of you now hoping that
NullType could indicate a method which tail-calls itself are out of luck, because the method could just
return null;. What you need is Neal's
Unreachable type, which is a true Bottom type because it's a subtype of everything and it's empty.
- Signalling errors. Java has exceptions. Next.
- A stand-in where no other reference type will do, as exemplified in the RFE. Here, the special subtyping properties of Bottom are less interesting than its emptiness. Neal's
Void type is useful here.
To summarize, the null reference makes NullType in Java weaker than Bottom, which in turn makes NullType less problematic than Bottom but also less useful. No other major programming language denotes NullType, let alone Bottom, so it is hard to claim that Java is falling behind by not having NullType. It doesn't make things simpler, nor radically expand the space of programs that can be easily written, so don't look for it in JLS4.