Evolving the Java language: generics were hard to implement

Some weeks ago, I watched a great presentation of Neal Gafter, Evolving the Java Language. If you have a look at his blog, the biography of Neal Gafter is:

Neal Gafter works for Microsoft on the dotNet platform languages. To balance his life, his hobby is designing and developing the future of the Java programming language. He was previously a software engineer at Google working on Google Calendar, and a senior staff engineer at Sun Microsystems, where he co-designed and implemented the Java language features in releases 1.4 through 5.0. Neal is coauthor of “Java Puzzlers: Traps, Pitfalls, and Corner Cases” (Addison Wesley, 2005). He was a member of the C++ Standards Committee and led the development of C and C++ compilers at Sun Microsystems, Microtec Research, and Texas Instruments. He holds a Ph.D. in computer science from the University of Rochester.

Neal Gafter

Neal Gafter

In the conference, he presents the challenge of Evolving a language. In the first part, he makes a come back on how Generics were implemented in releases 1.4 through 5.0 (autoboxing, for-each, enum and varargs were easier to add to Java –> easier doesn’t mean easy :-) ). He then explains what the short-terms goals are for Java (JDK 7.0) and what the long-terms ones are.

In a previous post, I’ve said that Java language is not adapted to concurrency and that, in the next years, some developers will probably migrate to another language for this reason. I’m glad to hear that this subject is addressed: Java leaders try to find a solution to this problem but, it is not trivial at all (and it will not be shipped in the release 7.0). At the end, Neal Gafter clearly says that Sun Microsystems doesn’t invest enough in the evolution of the language: I hope this point will change with Oracle.

A cup of <T>

Generic mug

When I watched this video, I recognized that I didn’t understand exactly why implementing generics was so complex. We use Generics when we manipulate collections but how many of us have already written generic classes or generic static methods? I develop using the Eclipse SDK and I see that Generics are not widely implemented in it. I don’t know all the Java frameworks so, maybe, some of them uses the power of generics: if you have ones, can you leave me a comment?

Investigating Generics

As passionate developers, we always want to go further. I investigated this problem with a great reference on Generics (not expensive and good reviews!):

Java Generics and Collections

Java Generics and Collections

My objective is not to give you a summary of this book but to introduce the complexity of adding generics to the language. Before generics, you would write this piece of code:

final List list = new ArrayList();
final Number numberInt = new Integer(0);
final Number numberDouble = new Double(2.2);
list.add(numberInt);
list.add(numberDouble);
System.out.println(list);

We can write List list = new ArrayList() because ArrayList is a subtype of List : we don’t care of the type of the managed objects. With generics, this code becomes:

final List<Number> list = new ArrayList<Number>();
final Number numberInt = new Integer(0);
final Number numberDouble = new Double(2.2);
list.add(numberInt);
list.add(numberDouble);
System.out.println(list);

So, we have added a parameter to the list and now, it is more specialized: only subtypes of Number can be managed by the list. We can write List<Number> = new ArrayList<Number> because the second type is a subtype of the first. But can we write the code below?

final List list<Integer> = new ArrayList<Number>();
list.add(new Integer(2));

In fact, this line will break the compilation with an error: “Type mismatch: cannot convert from ArrayList<Number> to List<Integer>”. The behavior changes with generics, the inheritance takes the parameter into account. This above code is wrong: list.get(index) have to return an integer but a Number can be a Double so, ArrayList<Number> is not a subtype of List<Integer>.And what about this code?

final List<Number> list = new ArrayList<Integer>();
list.add(new Integer(2));

The error is then: “Type mismatch: cannot convert from ArrayList<Integer> to List<Number>”. The problem is the opposite : list.get(index) returns a correct type (since Integer is a subtype of Number) but we would be able to add a double to an ArrayList of Integer!(list.add(new Double(3.3))).

Discovering wildcards

To understand wildcards, we can get a first sample : a method which prints the sum of a collection of numbers.

public static void printSum(final Collection<Number> numbers) {
if (numbers == null)
throw new IllegalArgumentException();
int total = 0;
for (final Number number : numbers) {
total += number.intValue();
}
System.out.println("Total of " + numbers + ": " + total);
}

And so, we can call it with:

public static void main(final String[] args) {
final List<Number> numbers = new ArrayList<Number>();
numbers.add(1);
numbers.add(2.2);
final List<Integer> numbersInt = Arrays.asList(1, 2, 3);
final List<Double> numbersDouble = Arrays.asList(1.1, 2.2, 3.3);
printSum(numbers);
printSum(numbersInt); // compile-time error
printSum(numbersDouble); // compile-time error
}

As you see, we have two compile-time errors and you know the reasons : List<Integer> and List<Double> are not subtypes of List<Number>. But, we want that the printSum method work with collections of integers and doubles. It’s the time to think about wildcards:

public static void printSum(final Collection<? extends Number> numbers) {
if (numbers == null)
throw new IllegalArgumentException();
int total = 0;
for (final Number number : numbers) {
total += number.intValue();
}
System.out.println("Total of " + numbers + ": " + total);
}

We’re using <? extends Number> now, and the code is safe. We have to understand the parameter as “a collection of subtypes of Numbers” and so, we can read its content but we can’t add objects:

public static void printSum(final Collection<? extends Number> numbers) {
if (numbers == null)
throw new IllegalArgumentException();
int total = 0;
for (final Number number : numbers) {
total += number.intValue();
}
numbers.add(total); // compile-time error
System.out.println("Total of " + numbers + ": " + total);
}

It fails because we don’t know whether it is a Collection of numbers or of one of its subtypes. To forbid the addition of an Integer to a list of Double, it is good that the compilation fails here. There’s no magic stuff here, the declaration of the method “add” is: boolean add(E e); Since E is <? extends Number>, we can’t determine the type and so, we can’t use the add method (only list.add(null) compiles).

It’s time to stop discussing about the features of generics. I just wanted to show that it required some major changes in releases 1.4 through 5.0: redefine the subtyping, update compilers and tools.

Maintaining the binary compatibility

Adding the features are hard but it is possible. But Java is a mature language and so, the compatibility needs to be preserved between a binary of 1.4 and a binary of 5.0. Implementors of generics had to find a way to compile the generics type in a bytecode compatible with 1.4 and 5.0(in the book, there’s an entire chapter on “Evolution, Not Revolution”). It’s the reason why erasure was chosen for new features. In fact, List<T>(5.0) and List(1.4) produce exactly the same bytecode but it’s invisible for the developer.

Frustrated

The future

After watching the conference of Neal Gafter and reading this great book “Java Generics and Collections”, you have a better understanding of the reason why evolving Java is so hard and time-consuming. Language leaders have to think about everything and I admire the quality of their work. Just remember that the language evolves but the bytecode don’t.

To come back to the presentation(slides), the short-term goals are:

  • Regularize Existing language
  • Improve diagnostics vs generics (better description of errors with Generics -> improve the compiler)
  • Fix type inference (being able to write Map<String, String> map = new HashMap<>(); -> reduce verbosity)
  • String switch (use string in swith statements -> currently, switch only works with primitive types)
  • Limited operator overloading (prefer mySize >= yourSize rather than mySize.compareTo(yourSize) >= 0)
  • Limited catch clauses (being able to catch more than one type of exception in a catch clause)
  • Modularity

And the long-term goals are:

  • Regularize Existing Language
  • Reification
  • Further Generics simplification
  • Concurrency support
  • Immutable data
  • Control Abstraction
  • Closures
  • Actors, etc
Share and Enjoy:
  • Digg
  • del.icio.us
  • Facebook
  • Google Bookmarks
  • Reddit
  • Yahoo! Buzz

Comments 3

  1. bob wrote:

    The implementation of Java generics assures that noone will use them for anything but collections cast reducing. Its a bogus implemenation and the “complexity” argument is an apology.

    Posted 10 May 2009 at 3:24 am
  2. Sakuraba wrote:

    So why dont we fix it then? Could the Oracle aqcuisition postpone the release of JDK 7 long enough to provide enough time to fix this?

    Posted 10 May 2009 at 11:40 am
  3. coder-friendly wrote:

    Hello Sakuraba,
    I don’t know if generics can be fixed with using reifiable types. Maybe, the cost to change it would be too high and that’s it. I don’t think that the current solution is as bad as Bob says. Maybe, there’s no many problems where generics are so useful (I think collections cover the majority but I may be wrong).
    I hope Oracle will invest massively in Java, but I don’t know whether they want to postpone the release of JDK7. But, they can be ambitious with the JDK8 :-) .
    Thank you for your comment.
    Coderfriendly

    Posted 10 May 2009 at 8:30 pm

Trackbacks & Pingbacks 1

  1. From Java Collections Cheatsheet | Coder-friendly on 12 May 2009 at 11:22 pm

    [...] Collections Cheatsheet Hello, As I said in my previous post, I currently read this great book (not expensive and few hours to read it): Java Generics and [...]

Post a Comment

Your email is never published nor shared. Required fields are marked *

Additional comments powered by BackType