What’s Next for the Java Language?
With Java 5 having been out for a while now, I started to think about what might be next for the Java language. I’m not thinking about what might change with JEE (nee J2EE), or the extensions or libraries or anything like that. I’m thinking about the core language itself. There were not a lot of changes between Java 1.1 and Java 1.4. The assert keyword was added and there were a number of additions to the core libraries such as NIO, but again, I wanted to think about the language itself and not the libraries so much.
What’s New in Java 5
Java 5 saw a lot of changes many of which, at least on appearances, seem to be a response to the .NET platform and the C# language. A little bit of healthy competition spurred on some advancements in the language. To start, I wanted to think a little about these recent changes because there are some really great language enhancements in Java 1.5.
Type safe Enums
Type safe Enums have added a full-class citizen of the Enum Pattern to the Java language. This is in many ways a better implementation than the C# version in that they are full classes that can have methods and data that belong to them as well. As is often the case with Enum types there are a number of associated helper methods that pertain to the Enum and the class itself is the most logical place for it to live.
Generics
Generics offer compile time checking that allows you to create type safe but generic classes. The most often used example of this is type safe collections classes.
Varargs
Varargs allows a method creator to accept a variable number of arguments to a method. This means the the user of the method does not have to manually create an array of arguments (it lets the compiler do the work for you). This is a handy extension.
Annotations
Annotations allow for an easy way to add metadata to classes, methods, etc. This is great for tools and frameworks such as unit testing and extensions like aspect-oriented programming.
‘Enhanced’ for loop (read foreach loops)
The new for loop provides an easy way to iterate over a collection without having to manually test exit conditions or do type casting.
Autoboxing
The automatic conversion of Objects to primitive scalar types and vice-versa.
(Most of the features are backwards compatible with previous JVM versions once they are compiled, but they are also areas where there is potential for non-backwards compatible optimizations in the future.)
What’s Next for Java 6?
I won’t claim all of these are likely or for that matter even good ideas. This is my attempt to brainstorm some possibilities for discussion purposes.
Full Closures
Java has support for a form of closure using anonymous classes, but Full Closure support is not there. Full Closures are similar to pointers to functions in some ways. It allows libraries to allow for the customization of behavior by passing a closure as an argument to a method call. A common example of this is a filter passed to a method that iterates over a collection to return a sub-collection. Full Closures also allow the sharing of variables both in a code block and out of the code block (so changes in one area effect the other). With anonymous classes in Java, the variables passed into the method have to be final to that method. Full Closures have been re-popularized by languages like Ruby and Python and of course older languages like Lisp.
Functions as First-Class Citizens
Full Closures often are implemented as functions. This might require the idea of having functions as first-class objects. This is not as crazy as it sounds, this is done in other programming languages such as JavaScript. Of course with functions as first-class objects it also opens up the possibilities of dynamically adding methods to an object or class, like JavaScript or delegation features like many other languages including C#.
Coroutines
Subroutines are a special case of a Coroutine that have only a single entry and exit point. Coroutines on the other hand have multiple potential entry points. A Coroutine will “remember” where it last left the routine and will begin just after that point when the routine is again invoked. This is a very helpful way to build things like state machines without having to do a bunch of if-then conditionals in a method. See the yield statement in Python as an example.
Allowing the Separation of Object Allocation and Initialization
The new keyword in languages like Java and C# perform two functions:
- Allocate memory on the heap for the object to live.
- Initialize the object
A constructor only allows you to control the initialization of the object, not the allocation aspects of that object. The allocation is a low-level routine that is done for you. Some languages like Objective C separate these constructs [[Object alloc] init]. Allowing some control over the allocation would allow for built-in support for things like Factory patterns and even for things like distributed objects by instantiating an instance on a remote system and returning a Proxy to that object.
(I think this one probably falls into the ABSOLUTE least likely to happen, but it’s cool to think about.)
Deterministic Disposal
Java has the finalize() method which is called prior to garbage collection of an object. Garbage collection is not deterministic in that you can not know when a specific object will be GCed. The finalize() method is therefore only useful for special cases regarding memory cleanup.
Disposal (ala C# IDisposable interface) on the other hand can be used to cleanup any resource because the dispose() method is called when an object goes out of scope. This can be used to cleanup non-memory resources like IO or SQL connections, rollback uncommitted transaction, whatever you want.
Some Things I Hope They Leave Out
C#-esque Properties
Properties are a shortcut for creating accessors and mutators (getters and setters) in C#. Properties are really compiled into methods behind the scenes. Why does this matter? Because implementation sometimes matters and can have side effects. Direct access to an object variable is not the same as accessing an object through a method. Going through a method the object is accessed through a copy of a pointer (pass by reference vs pass by value issues). While this is not a common issue you would run into, if you ever did figuring out these interactions could be much more difficult.
x.Arg = new Object():
Object y = x.Arg;
x.Arg = new Object();
If x.Arg is a direct variable access, then Arg and y will be the same value in the end because they point to the same memory location. If they are methods, then they will be two different objects. Weird huh?
What Else?
There’s a constant battle between extending the language with useful functionality and keeping the implementation clean and true to a coherent design philosophy.
What am I missing? What can or should be added to Java that would improve the language, but not destroy it’s nature?
I love most of the features in 1.5, Autoboxing being by far my favorite. If everything is an object, then why not have EVERYthing be an object? Varargs are cool, and allow great flexibility without destroying type safety. Enums are nice to have, but in .NET I always end up using the ‘java enum’ pattern anyways. I always want to say so much more about a particular status or label, most of the time I would like to have a user viewable version of that enum. Why can’t they implement enums based on strings? Annotations seem nice to intellectually but I don’t ever find myself saying, “This is the perfect place for Annotations!”. I don’t use them. And then Generics…………..
I CAN’T STAND GENERICS
They make no sense to me. Maybe I haven’t had so much experience with them because .NET doesn’t have a similiar type of thing, but Generics to me go one step too far in the battle for type safety. To me, if you don’t know what is going to be in a certain arraylist then you don’t understand what the system is doing. Generics add COMPLEXITY to development in that you have to think through an extra set of technical implementation details when you really want to be thinking about the problem and how to solve it. The complexity is unneeded and detrimental to a languages ease-of-use.
The language features that you list for 1.6 all seem to fall into a category of languages that I have seen described as ‘Freedom Languages’. C# and Java are business languages in this categorization. Ruby, Python, Smalltalk, etc are freedom languages. If you have two sons, one trustworthy, one decidely not, then you may give them each a different level of freedom because of the likely consequences. One can’t be trusted with the car, one can. One can’t be trusted babysitting the sister, one can. In the business world, you can’t tell which programmer can be trusted and which one can’t, because typically your experience with that individual can be limited. The consequences of giving an untrustworthy developer can be disastorous. Bad programmers do so many funky things anyways, can you imagine what they would do with Full Closures, Coroutines, etc?
Freedom is needed by good programmers, and freedom helps the programmer to flourish, to dream up solutions that are ever more elegant and useful. Freedom languages are needed by good programmers as an outlet for their personal or open source projects so that they can implement ideas more quickly and with more butter. There is so much risk though in the business(millions$$$$) world that the advantages of freedom languages and features are outweighed.
PROPERTIES
I think properties as an implementation detail may be redundant. They do though have a tendancy to communicate to developers the make-up of a state of an object. The ‘Get’ word can mean different things and has the possibility to confuse what is a method accessing state and what is a method performing some action. The real world command ‘Get me some milk’ can have many different steps behind it, you could be getting in your car and going to the store, or you could be opening the refridgerator, pulling out the milk and checking for it’s expiration. The possibly exists to put many different steps behind a property, for sure, but this is typically done when wanting to present the action as state, rather than action (ahhhh abstraction). Subtle difference? Large difference? What does the tech blogging world or Geoff think?
Enums in Java are improved over Enums in .NET. Enums in Java are a full class, so they can have methods and other data. Basically, they’ve built-in the Enum Pattern but added the flexibility that you can use them as Integral types for things like switch statements and the like.
Generics are wonderful! They increase the readability and intentionality of the code. They allow other people that are using your code to more easily know how to use it, and better than comments they are compile time checked. So, if you don’t know what a collection takes, the generics force you to know.
(By the way, C# 2.0 has Generics and it was just released to MSDN on Oct. 28th)
With Generics:
vs. Without Generics:
The other thing is that Generics are not only useful for Collections classes, but can be used by other, more general classes. It allows you to provide general functionality and an implementer to declare their own type safety mechanisms.
Again, with something like this you don’t have to test if the object passed in is both a Person and Comparable, you know that the compiler has confirmed this for you.
In the end, I think generics have the possibility of reducing errors and increasing readability of the intentions of the programmer, both of which are Good Things &mark;.
About finalize(): Not only do you not know when a GC will be performed, it is possible that a GC may NEVER be called. This is one of the reasons that finalize() is relegated to the same chapter as the GOTO statement. :)
One can guarantee (to some extent) object cleanup by using a try/finally block, but that puts the responsibility on the consumer of a particular class, not the class’ designer. Obviously the dispose() method comes from the C++ destructor concept, which never really got ported to Java (other than the half-a$$ attempt called finalize().