Readable Java 1.5
by Stephen Jungels09/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:
eachofcan 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(); } }eachofloops are readable and easy to understand. Theeachofkeyword attaches to the nearest enclosing loop, in the same way thatbreakandcontinuedo.
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 |