ONJava.com -- The Independent Source for Enterprise Java
oreilly.comSafari Books Online.Conferences.

advertisement

AddThis Social Bookmark Button

Readable Java 1.5

by Stephen Jungels
09/24/2003

The Java 1.5 proposal offers programmers a false choice between desirable new features and readability. In fact, all of the proposed new features for 1.5 can be represented by clear, unambiguous, and readable constructs without breaking backwards compatibility. In the rest of this article I will illustrate this point by describing three alternative syntaxes I am proposing.

New for Loop

Because of frequent requests for a more compact loop construct that works with collections and arrays, Sun proposes a new for loop that has foreach semantics. Using an example from the Joshua Bloch interview, Sun wants to replace this:

void cancelAll(Collection c) { 
    for (Iterator i = c.iterator(); i.hasNext(); ) {
        TimerTask tt = (TimerTask) i.next();
        tt.cancel();
    }
}

with this:

void cancelAll(Collection c) {
    for (Object o : c)
        ((TimerTask)o).cancel();
}

The collection-iterating syntax is more compact, but the use of a colon to indicate foreach semantics is cryptic. (Is it supposed to resemble a squashed equals sign? The ale must have flowed like water when they came up with that!) There is a belief at Sun that foreach is so commonly used (it is a keyword in Perl, VBScript, Ruby, PHP, and Tcl) that adding a foreach keyword now would break a lot of code that uses it as a method or variable name. This is a strong argument, and I won't question it, since I am proposing a backwards-compatible syntax, but that doesn't mean that all other options are exhausted and we should resort to punctuation.

For example, why not simply add an eachof keyword that turns standard for loops into foreach loops, resulting in code like this?

void cancelAll(Collection c) {
    for (Object o = eachof c)
        ((TimerTask)o).cancel();
}

Unlike foreach, eachof is not used in any of the major scripting languages. For the very small group of people who might have used "eachof" as a method or variable name, there is the javac -source option.

Besides resolving the backwards-compatible keyword issue, this syntax has several other advantages:

  • eachof can be reused in other loop constructs. It can be used more than once in a loop when two or more similar collections must be traversed simultaneously. For example:

    void cancelAll(Collection c, d, e) {
        assert c.size() == d.size() == e.size();
        while ((Object o = eachof c) != null)
        {
            ((TimerTask)o).cancel();
            ((TimerTask) eachof d).cancel();
            ((TimerTask) eachof e).cancel();
        }
    }
  • eachof loops are readable and easy to understand. The eachof keyword attaches to the nearest enclosing loop, in the same way that break and continue do.

This is my proposal. Other people might pick another keyword. The point is that with a little research, it is possible to find safe new keywords; it isn't necessary to resort to punctuation marks to add a backwards-compatible foreach capability to Java.

Generics

The major new feature in Java 1.5 will be generics, which will expand the range of static type checking greatly and take out a lot of the drudgery of working with collections. Some argue that in adding generics, Java should follow the syntax established by C++ and the Standard Template Library, which would mean replacing this:

static void expurgate(Collection c) {
    for (Iterator i = c.iterator(); i.hasNext(); ) {
        String s = (String) i.next();
        if(s.length() == 4)
            i.remove();
    }
}

with this:

static void expurgate(Collection<String> c) {
    for (Iterator<String> i = c.iterator(); i.hasNext(); )
        if (i.next().length() == 4)
            i.remove();
}

However, part of the reason Java has succeeded as well as it has is that James Gosling learned from the mistakes of the C++ project -- when he omitted multiple inheritance of implementation, for example, or when he made all reference types heap-based.

Java should continue this practice by adding the generic types popularized by C++ with cleaner and simplified syntax and semantics.

For example, many programmers seem to prefer a generics syntax with curvy brackets. The Generic Java proposal argues that that isn't possible, because it would lead to confusion between type parameters and value parameters. With a little thought, we can find another solution: put the type parameter in parentheses before the parameterized element, resulting in code like this:

static void expurgate((String) Collection c) {
    for ((String) Iterator i = c.iterator(); i.hasNext(); )
        if (i.next().length() == 4)
            i.remove();
}

It might be said that the parenthesized expressions look too much like casts, but they appear in a different context than casts. Also, given the way Java Generics work (type erasure), it's not misleading for the construct to look like a cast. It can be thought of as a "declaration cast" that specifies in one place what cast the compiler should silently insert whenever extracting an element from the collection.

According to this proposal, the cast operator ((TYPE)) is generalized into a type expression. In assignment context it performs a cast. In declaration context it specifies a generic type parameter. The two usages do not overlap, so the kind of operation being performed is always clear.

It is interesting to note that this syntax closely resembles array declaration. In the following code fragment, for example,

String   []   strings1 = new String [10];
(String) List strings2 = new (String) List;

the List declaration and the array declaration are alike except for the absence of parentheses around the array type declaration. If Java 1.5 adds statically safe arrays, they can be distinguished from standard arrays by adding parentheses, thus unifying array declaration syntax with collection syntax.

To sum up, an inverted generics syntax has several advantages:

  • It leverages and generalizes existing constructs. The programmer's existing understanding of type casts aids the understanding of this syntax.

  • It is more readable than angle-bracket type parameters. This is a matter of taste, of course, and your taste may differ from mine.

  • It doesn't result in type parameters and variable parameters becoming "stacked up."

  • It reflects what actually happens in a generic system based on type erasure, such as Generic Java.

  • It starts to unify generic collection syntax with existing array syntax.

  • It maps simply to the standard Generic Java syntax, making it easy to modify existing tools.

The following example combines inverted generics syntax and the eachof operator. Where Sun Java 1.5 would say,

void cancelAll(Collection<TimerTask> c) {
    for (TimerTask task : c)
        task.cancel();
}

the proposed syntax would be:

void cancelAll((Timertask) Collection c) {
    for (TimerTask task = eachof c)
        task.cancel();
}

Pages: 1, 2

Next Pagearrow