If Java is the answer, what was the question?

Binary Critic

March 1997

Common as a Household Mop

Figure 1 shows Java taking off like a rocket - rivaling just about everything else in Silicon Valley for living in real time. Within a very few short years, Java, JavaBeans, and everything to do with Java will be pervasive. The technology will burn brightly for a time and then burn itself out. Before that happens, though, Java will be as common as a household mop.

Product hype is as much a part of the computer industry as celebrity is an essential part of Hollywood. Excellence often falls victim to PR. In the case of Java, it is particularly difficult to separate the PR from the reality. But, never fear, Binary Critic is here!

So, is Java really an improvement? Simply put, if today's languages are inadequate for today's software engineering challenge, then Java must be inadequate, too, because most of Java is merely warmed over C/C++. In spite of its sudden celebrity status, Java lacks many of the features needed to improve the dismal science of software engineering - just like its predecessors. How so? Here is my analysis - with a minimum of hype - on the pros and cons of Java.

Java as Dial Tone

My first program was written on a vacuum tube computer containing a fantastic 32K rotating drum storage unit and all of the paper tape I wanted. It ran 32-bit software - more than you can say about many Windows95 applications! I soon graduated to transistors, ALGOL, FORTRAN, Pascal, Ada, and C++. I should have been content with such rapid progress. But, I was a chronic complainer. I wanted more. I wanted a universal programming language that worked on any computer, any operating system, from anywhere.

My dream was shared by a band of nerdy programmers that hung around the computer center. In fact, it was such a famous dream that we coined it the UNCOL Problem - the problem of devising a Universal Common Language. Sound familiar?

The year was 1963, and the gleam in everyone's eye was ALGOL, because it promised to solve the UNCOL problem. Why can't software be constructed from a standardized, universal, platform-neutral language? As it turned out, it couldn't. Little has changed since those early days. Today I hear the same chant: Java will solve the UNCOL problem. It will become the dial tone of the 21st century - not!

Pascal happened. It gained a huge following because PL/I was too big, messy, and unruly - much like C/C++ is today. Then, Ada happened. Ada was supposed to be a better Pascal, but the Ada command economy failed because capitalism abhors a monopoly. Then, C++ happened. You get my drift.

If the past has anything to do with the future, Java-as-UNCOL has an uncertain future. In the meantime, Java will have its 15 minutes of fame. It will happen. And then what? The question is, what devastation will Java leave in its wake?

Java: The Next Legacy Language

Because of Java's adoption rate, and expected life cycle, applications written in Java today will have to be replaced or vastly modified within a decade. As Java ushers in a new era of technology, our responsibility as technologists is to ensure that future applications built on top of Java are well-behaved legacy systems.

When viewed in this light, the question of Java-as-UNCOL becomes serious. For example, within DoD, the Y2K problem will cost an estimated $30 billion, alone. Maintaining legacy COBOL systems is a major industry today, and holds many organizations hostage to 1970s technology. Legacy software is what keeps IBM, Andersen Consulting, EDS, and many Fortune 500 companies in business. It is the tail wagging the dog.

 

The Failure of Minimalism

Minimalism is one of the elegant goals of Java. It is a simple language, just as Pascal was a simple language - on purpose. But, we know from history that minimalism rarely succeeds. Safe, reliable, minimalistic Pascal failed because minimalism is interpreted by the market as "incomplete". Pascal was instantly tagged a toy language.

Java-as-general-language is also incomplete - lack of I/O, intrinsic functions, APIs to an OS, and other features may force a repeat of the Pascal story. It copied many of Pascal's minimalist attributes: byte codes, strong typing, restricted pointers, and meaningful keywords instead of cryptic symbols. Java may be headed down the same path blazed by the first portable language - Pascal.

In spite of many pleas for simplicity over the ages, minimalism has failed in the language domain just as it has in nearly all other segments of the software industry. Java may be virtuous as a better C++, but markets rarely reward "best-of-breed" technologies. Why should it reward Java?

What Was The Question?

Clearly, the software industry needs a new paradigm: Is Java the new paradigm? The word paradigm may be far too strong. Most of Java is watered-down C++ plus some retro-fitting of Pascal, e.g. strong typing, keywords, packages (from UCSD P-code version), and portable byte code. Java threads are vast improvements over UNIX tasking, but certainly nothing new. About the only thing in Java that borders on a new paradigm is the tagged applet. Sure, applets and servlets are something new, but there is no real-world requirement that says they have to be written in Java.

In fact, if Java is the answer, what was the question? What problem does Java solve that could not be solved before Java came along? First, some general comments, and then some specific technical details on Java versus alternatives.

Dog Food

What are some of the problems awaiting future Java programmers? First, Java still retains too many of the error-prone features of C/C++. Syntactic problems abound, but I mention only a few - a sample of the dog food being fed to Java programmers. Consider the bad C habit of increment and decrement. What does int i = ++i--; mean in Java?

In horrible C tradition, Java arrays start from zero, instead of one. How many future programmers will spend hours locating off-by-one errors? What is wrong with the number one?

Unruly scope rules in Java add insult to C++ injury. Scope, range, and synchronization constructs are overly complex, sometimes contradictory, and mostly poorly thought-out. There are no less than ten object modifiers in Java. More dog food:

public

private

protected

static

final

native

synchronized

abstract

threadsafe

transient

The sacrosanct public modifier completely breaks down encapsulation in Java. Static, final, and public are contradictory, confusing, and poorly motivated. Native is unforgivable in a language that lays claim to platform neutrality.

Like Pascal, Java eschewed I/O. Lack of a standard language is what eventually killed Pascal. Both Pascal and Java off-loaded input and output to ill-defined and incomplete libraries. As a consequence input and output libraries have already begun to flourish, creating a variety of Java dialects. Microsoft pounced on this flaw and is destroying the "Java standard" as I write this.

Thread Bare Java

The designers of Java's most highly touted feature -threads - missed a golden opportunity to make major progress toward greater reliability in future (legacy) systems. They got some things right, but left some deeper problems for future programmers to struggle with.

Light-weight threading in Java is generally an improvement over heavy-weight tasking in UNIX. It opens up many opportunities - for good and evil. Java reincarnates Knuth's atomic procedures as a mechanism to ensure mutual exclusion. But, Java does not go far enough. In fact, Java atomic procedures and light-weight threads can lead programmers down the road to ruin. Here is an example.

Suppose a program manipulates data stored in a double buffer as shown in Figure 2(a). Two threads are created: one thread copies data from L2 into L1 and eventually into the running thread, designated here as T1. Another thread does the opposite: it copies data from L1, into L2, and eventually into the second running thread, T2.

In Figure 2(a), threads T1 and T2 simultaneously access L1 and L2. In fact, the order of access is immaterial - the important thing is to avoid race conditions. In Java, a programmer uses atomic functions to ensure mutual exclusion, e.g. the synchronized modifier. Synchronized functions ensure mutual exclusion by allowing only one thread at a time to be active within a synchronized function.

A programmer might innocently declare a synchronized Java function like the one below (I have left out some details to simplify). The GET access function is declared within a list class, and then instantiated twice - once for list L1, and again for list L2:

class LIST {

synchronized public get( List L; char c){... }

....

L1 = new LIST(...);

L2 = new LIST(...);

Now suppose that thread T1 uses these objects in the following order:

get(L1...); get(L2...);

and thread T2 inadvertently uses them in the opposite order:

get(L2...); get(L1...);

The resulting double threaded program works most of the time, but not all of the time, as the interleave matrix of Figure 2(b) illustrates. Most of the time T1 accesses L1 followed by L2, and T2 accesses L2 followed by L1. This sequence is shown as vertical and horizontal dashed-line arrows in Figure 2(b). But once in awhile, depending on the time slice algorithm of the underlying operating system, T1 is interrupted immediately after gaining exclusive access to L1, and T2 is allowed to gain exclusive access to L2. When the time slice returns control to T1, T1 is blocked by T2. When the time slice returns control to T2, T2 is blocked by T1. The two-thread system is deadlocked!

This is a particularly nasty fault, because it may occur on an irregular basis - almost as if it were a random hardware error. It cannot be predicted, nor can it be easily discovered through ordinary debugging techniques. From the point of view of the programmer, there is nothing wrong with the code. It must be the hardware! Thousands of future programmers will spend hundreds of hours trying to locate such errors.

A Better Mousetrap

A better solution to thread control in Java would have been to adopt Path Pascal's path expressions. Path expressions have been around for 20 years, just like most of Java's other features. They can be placed in the interface portion of classes, and therefore, used by the compiler to check for synchronization problems. In other words, synchronization should be an interface specification, not a coding mechanism.

My example illustrates only a few of the problems lurking within Java - problems that will be cursed by future programmers as they maintain the tons of Java legacy code that is expected to accumulate over then next decade.

In fact, Java fails to deliver on a number of nagging problems that will continue to plague software development for years to come. If Java is to become a better mousetrap, it must address core problems of software development. These - and other problems - continue to plague the industry. Java does not address most of them.

• Requirements capture and specification has proven to be a "bridge too far." Java does not even attempt to address this problem, but will Java hype hinder progress in this realm?

 

• Over the past 30 years, nearly all progress in software development has be attributed to early defect removal. On the positive side, Java has Pascal-like strong typing, restrictions on pointers, and single-inheritance. On the negative side, Java is subject to C/C++ syntax problems, the fragile base class problem, and non-standardized APIs - to name only a few. Java takes only a small step toward early defect removal.

• The development cost per function point increases exponentially with size of application. It cost more than twice as much to develop twice as much code. JavaBeans component technology may be an improvement, here. Even so, other technologies promise similar improvements. There is nothing intrinsically better about Java than alternative solutions.

• Application development must change at the rate of Internet Time - approximately 18 months. This either limits the size, functionality, or both, of new systems. Java does little to accommodate Internet Time. However, when combined with RAD (Rapid Application Development), Java's platform neutrality may make it possible to build some systems within Internet Time - systems that may not have been possible without RAD.

• Today's applications are vastly more complex than previous applications. Tomorrow's applications will be even more complex and demanding. In general, society's expectations are rising faster than the ability for software technology to deliver. Java does not appear to be a bigger intellectual lever than Ada, Pascal, and siblings. Even worse, Java does not advance the intellectual frontier. [Functional, Graphical, Rehearsal, and other languages may or may not be an improvement, but they also have not been given the same opportunity to prove themselves].

A Parting Shot

The error-handling construct: try-and-catch, single-inheritance, and interface specification constructs in Java should be applauded. Java has wisely added beneficial features of pre-C/C++ languages, and innovated the idea of an HTML-tagged applet. So, progress has been made even though it has taken twenty years.

I think this is far too little progress, however, for a grown-up industry. There are simply too many disappointments with Java at this early stage of its development. While there is hope that Java will eventually mature, I cannot recommend that we turn our future over to Java - quite yet!

Figure 1. Mainstream adoption rates of prominent technologies.

Figure 2. Double buffer problem, and the interleave matrix showing a deadlock state.