Java's Casts of Thousands

Conrad Weisert
©2001, Information Disciplines, Inc.


More than any other programming language Java forces programmers to embed hard-coded knowledge throughout their code about the types of data items. The numerous explicit cast operations in a typical program, not only annoy the programmer but also contribute greatly to the unreasonably high cost of maintaining Java software.

There seems to be little the individual programmer can do about this. Indeed, many articles and textbooks on Java programming techniques advise us that such repetition is not only necessary but desirable.

It starts with the primitive types

Good news!
Update--release 1.5

The addition of generic typed collection classes to Java has eliminated many of the most tedious, irritating, and error-prone needs for casting. If a collection can contain only items of a given type, then you no longer have to cast every retrieved element of the collection to that type.

It appears that Java may now become a practical language for developing large programs. For details see item 21 in Joshua Bloch's Second edition.—CW, May, 2009

In The Dark Side of Java, I noted that excessive casting, even among the built-in primitive types, rendered some of those types prohibitively clumsy to use. I've abandoned byte, short and float altogether, and I advise my students and clients to do so except where storage is critical.

Interfaces rendered less useful

Java promoters urge programmers to exploit the interface feature as a way:

Although a Java interface is only a contract and doesn't support code reuse, the latter advantage is a strong attraction. Alas, when you try to use interfaces for functions that return an object, you quickly discover that you have to keep casting the result to whatever class you know it's supposed to be. The standard Cloneable [sic] interface illustrates the problem. For example, this won't work:

      class Money implements Cloneable {
.
.
public Money clone()
{return new Money(this);}
}
.
.
Money amt1;
amt1 = amt2.clone();

The Cloneable interface insists that the returned value be an Object rather than an object of the class that implements it. Thus we must change the class and its use to something like this:

 class Money implements Cloneable {
.
.
public Object clone()
{return new Money(this);}
}
.
.
Money amt1;
amt1 = (Money)amt2.clone();

One might as well forget about clone and just invoke the copy constructor1 directly:

   amt1 = new Money(amt2); 

Defenders of this mechanism might point out that cloning is such an infrequent operation that we can live with such awkwardness. It goes much farther, however. In Conventions for Arithmetic Operations in Java I proposed standard function names for the overloaded operators that Java doesn't support. If we accept them, then the next logical step is to package certain arithmetic patterns that recur frequently. Here's a simple example for numeric types that can be added, such as Money:

 interface  Additive {
public Object add (final Object rs);
public Object sub (final Object rs);
public Object mpy (final double rs);
public Object div (final double rs);
public boolean equals (final Object rs);
public boolean lessThan (final Object rs);
public boolean greaterThan(final Object rs);
}
Now suppose class Money implements Additive. Then already clumsy user code such as this expression:
      shipChg.add(unitCost.mpy(qOrdered))
becomes this nightmare:
     (Money)shipChg.add((Money)unitcost.mpy(qOrdered))

Since the likelihood of persuading reasonable programmers to write such hideous code is zero, we've abandoned trying to exploit Java interfaces in capturing this and many other patterns. Keep in mind that the C++ equivalent is simply:

       unitCost * qOrdered + shipChg

Even equality is a mess

Java's notorious Object, the root of all classes, supports very little functionality, but the one useful method it supports is the comparison operator equals(Object rs). If you don't override it, you inherit its default behavior, which is a comparison of the reference values, like the == operator, which practically no class designer wants.

From the point of view of the user of your class, this is a simpler situation, since the function result is not of the class type but rather always boolean. Thus the client program doesn't have to cast the returned result.

However, for the class developer the situation is still messy. In the Money class, for example, you can choose between:

  1. Overloading the function:
     .
    public boolean
    equals(final Money rs)
    {return value == rs.value;}
    .
  2. and overriding the function:
     .
    public boolean
    equals(final Object rs)
    {if (!(rs instanceOf Money))
    return false;
    return value == ((Money)rs).value;
    }

Java zealots strongly prefer the second alternative, which "honors the contract" of the Object.equals method. I confess, however, that I'm going to break that contract, anyway, because another part of it says:

"You must override hashCode in every class that overrides equals"2

As everyone knows hashing is for identifiers and strings that might be conceivably used as key fields in a file or a table. To support hashing for amounts of money or most other numeric types is silly.

Although that messy code is localized to the Money class, it still has a negative impact on maintainability. In a complicated class, you'll end up with dozens if not hundreds of such casts.

Forgetting the original idea

Forty years ago we were trying to persuade hard-core assembly language programmers to switch to COBOL, ALGOL, or another early higher-level language. One of the main advantages we pointed out was the ability to separate the choice of a data-item's type from the procedural code that manipulates it. In assembly language you had to choose different instruction codes to manipulate different data types. In higher-level languages, you localized the dependency in a declaration.

Since then virtually every higher-level language honored that principle: PL/I, Pascal, C, Ada, C++, as well as modern versions of Fortran and Basic. But now Java comes along and forces the programmer to revert to assembly-language-like dispersion of type dependency throughout the procedural parts of a program. It's surprising that so few practitioners are complaining (so far) about this crude assault on program maintainability.

I know of no clean work-around.3 Don't be surprised if experienced team members ask: "Why are we using Java?"


1 - For most classes it's unclear why a clone method is needed at all when a copy constructor does the same thing.
2 - Joshua Bloch: Effective Java, p. 36.
3 - If I've overlooked something, I'd be delighted to hear from readers who have a practical solution.

Return to technical articles
Return to Java topics
Return to IDI home page

Last Modified, May 25, 2009