I have a high opinion of Java as a general-purpose programming language. No language is perfect though. Here I will list some perceived flaws. Note: a language with just a handful of flaws is a very good language.
In contrast to the basic Java language, the immense meandering Java libraries are very spotty. There are moments of brilliance, and better: moments of clear-headedness, but the moments one remembers are the nutty, stupid, and outright wrong ones.
Many higher-level languages, including C++ and C#, have a means to
control the responsibility for changing objects passed to subroutines.
In C++, this is done by the
const modifier keyword.
But Java has no such means. There is no way in Java to indicate
that an object passed into a method will or will not be modified by
There is a workaround, but it’s messy and inefficient (see programmer’s FAQ).
I regard this lacuna as being the single worst flaw in Java, and because of it, I consider the language unsuitable for large projects, and say as much to clients and employers.
It is interesting that, programmers in languages such as C++ which have such a feature, use it everywhere, and consider it good programming practice, while Java programmers extremely rarely use the workaround that would give the equivalent control. Why would that be?
I think it is a matter of convenience. For example, while one could do something like object-oriented programming in C, the language really doesn't have the necessary syntactic features to facilitate inheritance. So nobody (in their right mind) really tries to do OOP in C, even if they think it is a good thing otherwise. Likewise Java programmers rarely try to control responsibility of altering data, because they have to stand on their heads to do so.
Another story: when I was a Fortran 77 programmer, I had no appreciation for structured programming. All I needed in life was arrays, and to me, the structures of Pascal and C were just a weird cute thing that no self-respecting programmer would touch. I'm all changed now. And now Fortran 90 has structures (but most of its users just use it for the improved comments and formatting). OK not such a good example.
==operator means identical
The Java operator
== means “identical”, not “equal”.
For primitives, it is the same thing—for objects it is not.
Java variables are a hybrid of primitive and object, and one clear way
to implement a common behavior for
== is to say it means
In C++ terms, the
== operator performs pointer equality,
rather than object equality.
The Java developers were thinking in terms of pointers as references…
there are other instances of such thinking in Java.
But unfortunately, and the symbol looks like “equals”, and the Java
meaning is unlike our previous experience with
other languages, so this choice has resulted in mistakes and confusion.
Besides, in applications programming, equality of variables is much a much more common question than identity. It would have been better to have had an operator such as
== be overridable via the
With Strings, this can result in very confusing behavior.
Literal strings are instantiated at runtime as String objects.
Many VMs will instantiate equal strings only once (as an optimization)
so it will look as if
== compares string equality.
But that is not what’s happening.
Two separate but equal string literals may be mapped at runtime
to the same String object, so a comparison says they are identical.
The rule is: to check Strings or other objects for equality, use the
equals() method, not the
The standard objection to unsigned integer types is that in C, bad things happen when signed variables get cast as unsigned and vice-versa. Of course this is not an argument in the strongly-typed Java language.
I miss the counting numbers I had grown fond of in C++. To write tight Java code, I often have to write two check of integer arguments: is it too big, and is it too small.
In order to accommodate the loss of unsigned types, Java has an extra
bitwise operator, the unsigned left shift
If you mean your integers to represent a pattern of bits, the normal left
shift does not do what you want: left shift by 1 does an arithmetic
division by 2, which in the case of numbers with the minus bit set,
is not the same thing. So: not only must we keep in mind our interpretation
of the integers, but remember which shift operator to apply to them.
Typically, only one ever makes sense. If both signed and unsigned integer
types existed, the shift operator
>> could have been
made to do the right thing every time.
I miss enum’s. Neat, efficient way to make a small integer type. (Java 5 now has enumerated types.)
Do we miss multiple inheritance? Maybe sometimes. Nah.
It’s a drag to write a separate list class (and file) for each type of which one wants a list.
On the other hand, the C++ syntax for templates is horrid.
Could there have been a better way?
[Java 5 now has Generics. Have the shortcomings of C++ templates been overcome?]
Particularly, that static variables are global across instances of applets.
It would have been better to retain the traditional meaning of
static, and invented a separate keyword
(hey, how about
global?) for this other behavior.
Has meaning of C++
const for variables,
and C++ non-
virtual for member functions.
The Java libraries contain moments of brilliance, but what one comes away with is often a pretty disappointing experience. The style is almost as choppy as, say, X-Windows (which was written by many people over a longer period of time).
They really look like the work of different groups with not very good agreement or common management. One would think a big company could do a better job.
Like many aging actresses, every very few years they undergo a face-lift, and at the same time become much larger and less manageable.
I find that I spend 90% of my time on any given Java project, trying to get around some poor library implementation.
Below is just a sampling of some of my experiences with them. It is in no way complete, even as a record of my experiences.
One needn’t look far in the original Java AWT to find a design disaster. The first day of graphics programming will introduce a newcomer to Component/Container. These were intended to model a graphical containment hierarchy--windows and widgets that contain other windows and widgets, of course. But it got out of hand, and both of these classes are now monsters, that do all sorts of things besides model graphical containment: drawing, event handling, keyboard focus traversal.
Each of these functionalities really should have been individually modelled by special interfaces and classes. This would have been the modern approach: simple interfaces that model well simple behaviours. If something like the full-blown Component/Container were needed, it could easily be constructed from the simpler classes.
But worse: the model was made using concrete, instantiatable classes, rather than interfaces. This results in an inflexibility, which has plagued graphics in Java ever since. For two examples: see Swing and MIDP, both of which constitute attempts to fix the problem for different environments.
Pity about applet not having setMenuBar in 1.1. Sure it’s a design faux pas sometimes, but the capability should have been there.
Why on earth does applet have AudioClip, but no means of generating a sound?
Don’t even talk to me about Java events.
I am aware of 4 implementations of event API's in the Java libraries (and depending on how you want to count them). The related function definitions, classes and interfaces are scattered across at least 4 packages. And with all that, it is still very hard for a programmer to write cross-platform code, say, to respond to a key stroke.
There is the original Component
then its replacements
processEvent and the associated
AWTEvents and EventListeners. Things got ever more complicated, but
somehow, not closer to what I personally needed. In Swing, came
KeyStroke and JComponent’s
That wasn’t good enough, and is already deprecated. We are now to
strike me as a lot more interface than I wanted. Sun seemed to agree
with this, as they straightaway deprecated part of that interface.
But in the end, one still cannot write simple, cross-platform code to do one thing when a certain keyboard key is held down, and something else when it is released.
Sun was told of the problem dozens of times. In bug reports by many different programmers, they denied it exists. (And then implemented a new interface.)
Could it really be that hard? It’s as if, every time a new SDK project head comes in, their first goal becomes to reform the events mechanism, and since it’s their first experience in reform, the botch it up. That would be consistent with the observations, at least.
What function is really in charge of setting the various bounds of a Component? setLocation? the x, y one or the Point one? setBounds? setSize? This is really a problem if you want to override the default behavior of a component. There’s a similar problem with getBounds.
Graphics: The only functions that take a geometrical object are the Polygon functions. Shouldn’t all the geometrical drawing functions have taken geometrical objects as arguments? Sure would make for prettier code. Shouldn’t drawRect have taken a Rectangle argument? etc. etc. etc.
Point and Rectangle are sadly underdeveloped. Beside being under-used in Graphics, they lack many obvious functions.
Polygon is a disappointment. Shouldn’t it have taken an array of Point, instead of two arrays of int and another int to count them?
The sad, sad renaming of so many functions in 1.1.
The questionable default set of LayoutManagers. And shouldn’t the default LayoutManager for a component be one that doesn’t change anything? Why, yes. Instead, they all move and resize everything, including Buttons and such that shouldn’t be resized. The Java 2 solution is to make it all far more complex. It would have been better to start simple.
Popup Menus should have been in 1.1.
Absence of a selectable, inline text Component (as in a word processor, as opposed to a dialog text entry field)
Toolkit.getFontList deprecated in 2, moved to completely different class. Makes it difficult to write programs that depend on this that work in either environment.
Color: Why no toHTMLString() ? Why not a brighter( double amount )? Why no copy constructor? Why not a blend( Color, Color )? Why not parseColor( String, Color.StringFormat )?
Font: Why no copy constructor? Why a single integer style rather than separate style, weight, etc. as in other font systems? Which comes first in Font constructor, style or size? (Would have been better if style were a type, rather than an integer).
Frame: setIconImage is so messed up. First, getting the icon in an applet can’t be done through the Toolkit, only by the Applet method. Second, setIconImage doesn’t work consistently across VM’s. Doesn’t work at all in IBM 1.3? I’ve heard that in MS VM, calling it for any Frame sets the icon in all frames (?). Third, Dialog isn’t a Frame, and doesn’t have setIconImage, although it does have a title bar. The consensus is that it should inherit its icon from the owner Frame passed to its constructor, but I’ve never seen it happen in a Linux VM.
Visibility vs. size. Components don’t seem to get a size until they’re first made visible (or addNotify is called). This makes calculations on sizes of sub-components a terrible hassle.
Classes representing a list and hash and stack should have been part of lang.
Why doesn’t Boolean have
parseBoolean( String )?
And hey, wouldn’t it have been nicer if all of
Integer.parseInt( String )
Long.parseLong( String )
Float.parseFloat( String )
Double.parseDouble( String )
were just named
Tragically-named Vector class. Should have been a list or something. If someone wants to write an algebraic vector class, they have to call it something else. (I know, this is an old foul-up in computer science. But couldn’t some brave soul have stood up and objected? Maybe they did, and were taken out and shot.) Also, it’s so commonly used, it should have been in lang.