One Programmer's Opinion of Java
Most Recent Change: Mon Oct 5 04:57:36 1998 UTC.
I've used C++ since 1993, on a nearly daily basis since 1994. Last
year I began teaching myself Java, with the goal of getting a feel for
its power as an application (as opposed to applet) development
language. Here's a summary of my opinions. I don't claim they're
unique -- they're more of a vote than a revelation -- but I hope you
find them worth reading anyway.
What I Like About Java
- Threading is well integrated into the language and its standard
libraries.
- So is exception handling. In particular, it is much better
than C++'s exception handling -- Java ensures at compile time
that exceptions will be caught. C++ doesn't, and that's just begging
for trouble.
- There are many fewer platform- or implementation-dependent
features, from the source level all the way down to the binary level.
- Though much has been made of Java's (lack of) speed, I have to
say that it's not that bad, even without a JIT compiler. Its speed is
of course the result of an engineering tradeoff with
platform-independence -- which ultimately pays off for the end user in
fewer bugs (well, in theory, anyway). In my experience, I/O is Java's
only really painfully slow feature.
What I Dislike About Java
- Java forces a singly rooted inheritance hierarchy. Among other
problems, this leads to gratuitous, annoying, dangerous, excessive
casting.
- Java lacks multiple inheritance of implementations. This makes
some kinds of code reuse (e.g., mixins) far clumsier than they need to
be.
- Java's closest equivalent to destructors, finalize methods, are
not nearly as useful as C++'s destructors. In my C++ code, I can
easily create a class that grabs a resource (acquires a file lock,
sets the mouse pointer to the watch pointer, disables a window,
allocates some memory) in its constructor and frees that resource
(releases the lock, makes the mouse pointer normal again, enables the
window, deallocates the memory) in its destructor. Then you just
allocate one of these objects on the stack or as a data member, and
the resource will be released whenever the enclosing scope exits
(roughly, when the function returns or when the owning object is
destroyed).
Java's finalize methods are called at unpredictable times, so you
have no real equivalent -- you have to use finally blocks, which just
clutters the code and, because it's something you have to do manually
every time, is error-prone. In exchange, you get garbage collection
-- which is nice, but you can get much the same effect in C++ with the
technique described above, and the C++ technique is obviously more
general.
The essence of the problem is that C++'s destructors are
lexical and Java's finalize methods are not, and there are
some objects whose lifetimes naturally match the lexical scope (or the
lifetime of their owning object). Java's finally blocks can be seen
as an attempt to work around this limitation, and as such they are
insufficient.
- Java has no unsigned types, so robust code often must perform
sanity checks that you can entirely avoid in C++. More typing, more
code, slower execution, more danger, no benefit.
- Java has no templates, so you can't write classes that are both
type-generic and type-safe. You pay for this by writing code to wrap
the generic object with a type-specific interface, or by checking
everything at run time, or by taking your chances. Again: more
typing, more code, slower execution, more danger, no benefit. (Well,
maybe there is one benefit: heterogeneous collections are sometimes
easier. It's not worth it.)
- Java lacks operator overloading, so there are some things you
can't say in a natural way. You can't overload the binary * operator
to make a vector cross product or to do matrix multiplication, you
can't make function-like objects that are actually called as
functions, and so on. This continually intrudes on your consciousness
as you try to develop certain types of code, and it's irritating.
- Java has no ranged types (e.g., an int whose value may only be
between -17 and 42). C++ doesn't have these natively, either, but you
can fake it inexpensively with templates and operator overloading.
Because Java lacks those features, the analogous Java implementation
is a fright. This is yet another type-safety problem.
Conclusion
Despite misgivings, I've decided that Java is about as good a language
as C++. It's a lot more like C++ than
Sun
cares to admit -- almost as if you had rewound C++ to 1991 or so and
applied a different set of design principles from that point in its
evolution onward. (Which is very, very roughly what happened, in
fact.)
Java is very effective in its chosen niche -- small- to
medium-sized, non-speed-critical applications that need to be
cross-platform and may also need close integration with the Web (say,
to be delivered on Web pages). As the applications get larger, Java's
organizational weaknesses (e.g., its relative lack of type safety)
hamper it more. Of course, the fact that you theoretically don't need
to debug your Java application on multiple platforms means that you
may save in testing what you pay for in other ways.
In general, Java's restrictions (when compared to C++) make it a
little harder for bad programmers to write bad programs at the expense
of making it somewhat harder for good programmers to write good
programs. I suppose that whether you consider this a good thing
depends on how many bad programmers you have to deal with (maybe it
also depends on whether you're one yourself :-).
s-max@pacbell.net