Java and Pieces

Note: This article was originally a blog post entitled “Java stinks. Really.” I have since come to find Java a very good platform, and the Java language a reasonable and comfortable, if verbose, language to work in, so I no longer have the sentiment that it stinks. That said, the core criticism I make in this article still stands, and makes Java less useful in certain situations, and possibly less desirable for some programmers. The text, however, is unchanged.

I've never been a fan of those "Why XYZ is better than ABC language" posts that crop up all over the Internet. Usually, as soon as one is posted, someone else comes along and says that the first poster doesn't have a clue, and frequently they're right.

Also, I've been apprehensive of peoples' attempts to compare Java and C++. I've said for some time that anyone who says that Java is just like C++ doesn't know C++ and probably doesn't know Java, and I'm still sticking by that. They've got syntactic similarity (a lot, in fact), but their semantic similarity (which is what I believe is actually relevant in language comparison) is slim. Java is much better compared for similarities with Python or Objective-C, although it is stricter than either of those languages (take my Objective-C statements with a grain of salt; I've only dabbled in and read about the language without actually using it for anything).

Lastly, I consider Java to be a decent language from a design perspective. It is extremely clean (to a fault) and has simple semantics (again, to a fault). It takes care of many messy things for the programmer, and has a large and largely-useful library base.

All that said, here I am writing a post about how I consider C++ to be superior to Java in many respects. I do not promise that this will be tightly reasoned, or entirely rational. Bear with me.

Give me the pieces, please

My musings on this subject of late were tipped off when I was working on preparing the assignment for the class and was looking for a way to do a binary search on an array (or other sequential structure). So I went and looked in the Java API documentation. Sure enough, the java.utils.Arrays class has a binarySearch method, overloaded for all relevant array types.

One catch, however. The elements of my array were not guaranteed to be unique in the search characteristic (position). Multiple items could have the same position. It is not defined which item binarySearch returns if multiple items match the key.

OK, so I go and look for a multiple-keyed map. No such thing exists in java.util. Map is only allowed to have unique keys. So I created a map of lists of objects, and stashed my things in there.

All I wanted to do was have my array. Each iteration (I was writing a simulation) would do a fast sort on the array (changes are small from iteration to iteration, so the array would already be almost sorted and a bubble sort to quickly establish sorted order would get the job done quite rapidly), and then find the range of elements at a certain position (for finding neighborhood residents). If I were in C++, the lower_bound and upper_bound STL algorithms would give me the exact bounds of this range.

C++'s STL is built around the concept of providing the programmer with a variety of useful components of algorithms. The programmer can then assemble the pieces (various types of iterations, linear searches, binary searches, filters, etc.) into whatever algorithm they're trying to build. And everything is fully parameterized, so they work on any data type, with any comparison, that the programmer can imagine. Java provides a few things, but seems very limited in this regard. Further, it is inherently limited in some ways, particularly until it grows delegates, since it doesn't have first-class functions -- STL-style functors require a bunch of ugly boilerplate in Java. Granted, writing new functor classes for the STL isn't exactly clean, but once written using them is a breeze.

In this regard, C++ seems like the old, faithful Legos (and early Technics, such as the Dacta series). A variety of simple, dependable pieces that could be put together into anything you want. Java, on the other hand, comes with all kinds of fancy pieces that look like they might be something but in fact don't quite fit with the rest of your model. Kinda like the new-fangled Legos with pieces excellent for making a second-generation TIE Interceptor but worthless for anything else, unless you really want Darth Vader on your pirate ship.

Let me think

Java does a variety of things to take burdens off of the programmer. This is a laudable goal -- programmers should be able to be free to think at a high level about how their programs work without getting too bogged down in details.

But Java takes this a bit far. I present two examples: memory management and exception handling.

Memory management

Java is famed for its garbage collection. A garbage collector is a good thing in many cases. It's also a notable feature of many excellent languages -- Python, OCaml and most Lisp dialects come to mind. But it isn't necessarily what you want all the time.

Java gives you exactly two kinds of memory: stack memory for trivial values (ints, doubles, references, etc.), and heap memory. Heap memory is garbage collected. Heap memory is the only place you can place objects.

The majority of the time this isn't a problem. But C++ lets you choose -- you can place objects in the heap or on the stack. And, in modern C++ code, memory management isn't a big deal. You can wrap heap-allocated object access in reference-counting smart pointers (or smart pointers connected to some other garbage collection strategy), and your memory allocation issues can largely go away (not entirely, you still have to use your head, but most of it goes to the background).

The point is, C++ gives you a choice and makes you chose. Java makes you use their choice.

Exception handling

Java's exception handling isn't bad. The fact that the compiler requires you to deal with potential errors isn't bad. But it isn't as good as it can be. Especially when you're dealing with trying to make code exception safe (i.e., doesn't leak resources [such as open files] if an exception is thrown through it).

In Java, you accomplish this with try...finally blocks. If you open a file, you wrap some other code in try...finally and close the file in the finally statement. Not too bad, but you have to recognize when you need to do it and consciously put it in place.

C++ has no finally. Some people want to add it, but IMO that would be a Bad Thing. C++ has something far slicker -- RAII (Resource Acquisition Is Initialization), a bad name for a good system. Basically, since you have the option of allocating objects on the stack, and destructors are fired at the point when an object goes out of scope, you use well-written classes with destructors that clean up for you if an exception is thrown through your code.

One example of this is the C++ iostream library. File stream destructors close their file. So, if you create a file stream, and open a file for output, and an exception is thrown, the file is closed on the way out. Without the programmer having to do anything special.

Exception safety in C++ requires foresight and thinking. But it requires a different kind than Java. A programmer must be aware of exceptions, and carefully design their classes to have good behavior. They probably need to use a variety of helper classes, and perhaps write some of their own, that have no purpose other than exception safety.

Once this is done, however, the programmer's job becomes easier. Rather than think about where cleanup needs to happen or where exceptions might be thrown, one just writes all their code as if an exception could be thrown anywhere (with a few exceptions, it can). Use these tools, and make sure all resources are either used properly or released. C++ lets you create the tools to define good behavior in the face of exceptions, and then uses those tools to make your life easier. That is a good programming language.

Values and objects

Another gripe I have with Java is that it uses objects for (almost) everything. You want a list? It's an object. You want a string? It's an object. The only values are primitives (ints, doubles, chars, references, etc.) This issue is related to the memory management/garbage collection aspect -- garbage collection is excellent for objects, but not so great for values.

C++ lets you define new, first-class value types. Complete with their own operators. Don't like the behavior of a pointer? Create a smart pointer type which acts in most regards just like a pointer, but encapsulates your favorite behavior (such as reference counting, or weak referencing). Create a string that is fully integrated into the language (std::string does this).

Java doesn't. 'Nuff said, almost. Some things are naturally values. Strings are the biggest example that comes to mind. What makes matters worse, though, is that Java cheats -- it includes concatenation as a supported operator on strings. Lists are also values in most cases.

Java doesn't have any kind of operator overloading. IMO, this is a good thing, but not for the reasons commonly presented (abuse, etc.). Operator overloading doesn't make sense on objects in the OO-sense of the word (unless you're in a language such as Python, where everything, including ints, is an object and there are no distinct value types). Java has value types, with operators, but doesn't let the programmer create their own.

Conclusion

I hope you'll pardon the ranting and rambling style you've just dug through. It's been helpful to me to get these things out and in front of me.

And I don't get me wrong, I still think Java has some elegance as a language, and some sheer brilliance as a virtual machine. But it has its flaws, and those flaws have been particularly aggravating of late.