That's okay: a year later Java still sucks
When I programmed in Java, it just made it incredibly irritating to do all the things you typically want to do in a program. Say you want to store a whole heap of integers, in a long list, populating the list and then perhaps performing operations on it. To create the list, in Java, you go,
ArrayList mynumbers = new ArrayList() ;
In C++, for instance, it's,
vector<int> mynumbers;
Not so bad either way. To add the number '5' to the end of the list, in Java it's,
mynumbers.add(new Integer(5)) ;
Which is starting to look a little awkward. This is due to the inelegance of primitive types not being treatable as objects, so it has to be 'wrapped' in this Integer type.
In C++ it's as easy as one could expect:
mynumbers.push_back(5) ;
C++ also has better type checking here. In the Java version you could accidentally add something which isn't a number to your list of numbers, and then later on, boom. The C++ compiler will guarantee that you really do add an integer to the list of numbers.
Then we come to the real killer when you want to get, say, the 3rd number back out of the list again, in Java it's,
((Integer)mynumbers.get(2)).intValue()
While in C++ it's,
mynumbers[2]
You call a language which requires the above construction just to get a number out of a container 'elegant'?
Of course to make your code truly robust in Java, you are going to have to catch the bad cast exception that could occur if the object in the container isn't really an Integer. (Of course, you can just write your code asserting that your container always has only Integers in it, however either way, it's more burden on the programmer). C++ will guarantee that the container only ever contains ints.
This kind of horrible casting everywhere is the anti-thesis of 'elegance', and was horribly irritating when I had to program in Java, and is horribly irritating to me now that I have to program in C# (which is very similiar to Java, but worse, because it leaves out Java's only mildly redemptive feature).
Then there's the way Java (mis)uses exceptions. It's a nice feature that Java has well-defined behavior on an array-out-of-bounds error. The problem is what that behavior is: anytime I make such a programming error, I want to get an immediate assertion failure of some type, hopefully with a nice stack trace. Instead Java throws an exception.
Why would someone want to throw an exception on a programming error? Exceptions are useful if you are going to handle an error and perhaps recover from it. If your own program has a bug in it, there's no way you can gracefully handle it or recover from it. Exceptions are useful if your database fails, or if your network connection dies, or the system runs out of memory, or the user enters bad input. They are not useful if your program has a bug in it. Why the Java designers think they would be is beyond me...
Further, making your program exception-safe in Java is a nightmare. I have frequently had to have nested try...catch...finally blocks to do it, and verifying that all possible code paths will satisfy your desire exception-safety requirements is very difficult. Further, since so many things can throw exceptions in Java, guaranteeing transactional exception safety semantics (i.e. if a function throws an exception then it will leave the program state otherwise untouched) can be very difficult.
As an example, suppose you have a function which is going to call three operations: op1(), op2(), and op3(). Each of these operations will throw an exception if the operation fails. If an operation fails, you want to ensure that your function exits with none of the operations having completed. You have the functions op1Rollback(), op2Rollback(), and op3Rollback() which can rollback an operation that has been done. Doing this in Java requires nested try...finally blocks. It can be implemented in C++ without any try blocks at all, and with lots less headaches for the programmer.
That's not to say C++ is the greatest programming language either. It has issues, big issues. Its difficulty to learn being the most prominent. Its undefined behavior and inconsistent implementations also being severe problems. It is elegant in some parts, but ugly in others.
The only truly 'elegant' languages I have used aren't used nearly enough in practice - languages like Haskell and Lisp.
-Sirp.