I loved this recent post from Bill Clementsons blog. It finally made me realise why Java drives me nuts. It's simple: the observation is made that C is ideal for programming a von-Neumann machine - it's a light abstraction of it's capabilities into human readable language - flow of control keywords, and a strong set of arithmetic operators take care of the CPU and ALU. It interfaces well with the host O/S because the host O/S is itself written in C, and it just treats memory as a huge flat, address space. Indirection is achieved via function pointers, so we can treat functions as first-class objects, sort of.
Lisp is at the other end, it's deeply emmeshed in the Lambda calculus wich appears to be an elegant model of computation - don't quote me on that, though, I'm only just beginning to learn this. However, the upshot is that it has powers of abstraction that leave other languages panting.
And Java. Well, Java. Java seems to be all about the notion of the object; at it's root, the absract data type. We lose the ease of interaction with the von-Neumann architecture that we gain with C, in return for this object abstraction and a huge class library. It almost pulls it off. Guy Steele called it "halfway to Lisp" but I think that was being generous. It's more like a third of the way to Lisp, given the lack of flexibility in constructing abstractions. Generics might even the balance a bit, I'm not yet sure: however, as they are not a compile - time construct, I doubt it.
Sometimes it's good to be freed from the von-Neumann architecture: but I'm not convinced that the object abstraction, on its own is enough. Interfaces are a poor substitute for first class functions and closures. It must be said that a disciplined programmer can do the bookkeeping needed by C without thinking, too much. All pointers need to be set to NULL in debug after being free'd(). Pointers that are going to point to something dynamically allocated must be initalised to NULL: usually this is enclosed in a suitable macro. There are perfectly good tools likeValgrind and Dev-Partner for tracking memory usage. Modern architectures make the dangling pointer/memory issue a thing of the past. If the tools availble are properly used.
I find Java excels where objects excel: constructing models of real-world processes where objects map nicely to concrete entities: GUI's are a good example. When you are dealing with a more amorphous problem, Java is suicide. There is a reason why the OO 101 courses always tend to show students GUI code as an example, or simple simulations. Those are the problems that classically, objects were designed to solve, and they solve them well.
In short, Java reminds me of Pascal; a language with training wheels that give you just enough power to write a decent program, but not enough to give you the rope to tie yourself in knots. In some environments, this would be an excellent thing, but I've been ruined forever by Lisp. Java will always be my fourth choice, after Lisp, C++ and C.
I know what I'm missing. Sniff.