Readable Java 1.5
Pages: 1, 2
Variance
The latest proposed feature for Java 1.5 is variance, which fixes some flaws in Java's type system and increases the range of static type checking. An introduction to variance is beyond the scope of this article, so I can only refer the reader to the variance white paper and the variance tutorial included with 1.5 Early Access, and move on to the syntax.
The proposed syntax for variance denotes:
- A covariant, or read-only list of
Numbers asList<+Number>. - A contravariant, or write-only list of
Numbers asList<-Number>. - A bivariant, read-write list of
Numbers asList<*>. - An invariant list of
Numbers asList<=Number>.
The syntax for statically safe arrays is similar.
I submit that this syntax is confusing, especially for the programmer still trying to grasp the meaning of variance.
Instead of trying to map the types of variance to arithmetic symbols, let's
refer back to a basic feature of inheritance: a superclass precedes its
subclasses, chronologically. Given that fact, we should look for a way to
represent chronology typographically. The idea of "reading order" jumps out
right away. In English, at least, we start reading at the top left, and
continue down and to the right. A suitable syntax that agrees with these
observations is to represent a Number and its superclass
ancestors, for example, as "..Number", and to represent a Number
and all potential subclasses as "Number..". For clarity, these
typeranges can have explicitly closed versions: "Object..Number"
and "Number..final". In either case, the relative position of
"Number" and the two-dot ellipsis determines the direction in the class
hierarchy signified.
Having introduced this construct, it is interesting to look back and notice
that it isn't unprecedented. Java and other C-like languages already use this
kind of positional notation when distinguishing between preincrement
(++i), and postincrement (i++), for example.
Here is an extended code sample from the variance tutorial, translated into my proposed syntax:
public abstract class Shape {
public abstract void drawOn (Canvas c)
}
public class Line extends Shape {
public int x, y, x2, y2
public void drawOn (Canvas c) { ... }
}
public class Polygon extends Shape {
private (Line) List lines;
public void drawOn (Canvas c) { ... }
}
public class Canvas {
public void draw (Shape s) {
s.drawOn (this)
}
// Covariant (read-only) list of shapes
public void drawAll ((Shape..) List shapes) {
for (Shape s = eachof shapes)
s.drawOn (this)
}
}
...
// copy from covariant list (read-only) to contravariant list (write-only)
public void copy ((Shape..) List from, (..Shape) List to) {
for (Shape s = eachof from)
to.add (s);
}
// copy from covariant array (read-only) to contravariant array (write-only)
public void copy ((Shape..)[] from, (..Shape)[] to) {
int i = 0;
for (Shape s = eachof from)
to[i++] = s;
}
This syntax has several advantages.
-
It can be compact enough to satisfy somebody writing complex type expressions. The short form of covariance is
(Shape..)and the short form of contravariance is(..Shape). -
For beginners, a more explicit and verbose syntax is available:
(Shape..final)and(Object..Shape). Without needing to understand the underlying type theory, a novice programmer can easily determine what kind of objects are permitted as method arguments, for example. -
The positional notation is reinforced by Javadoc, which shows subclasses below and to the right of superclasses.
Conclusion
As I hope this article has shown, there are readable alternatives to the syntax developed by Sun for the new features proposed in Java 1.5 — alternatives that can maintain backwards-compatibility with standard Java.
What do you think? As Java programmers, do we need to compromise readability in order to get desirable new features? Are there realistic alternatives to Sun's proposal? Is the author's proposal readable or awkward? Does it improve on Sun's proposed syntax?
Extended Examples
For comparison purposes, here is an extended example in Sun's proposed 1.5 syntax and the author's variation on that.
Java early access 1.5:
import java.util.LinkedList;
import java.util.Collections;
import static java.lang.Math.*; // import static
class Test {
// enum
enum Color { red, green, blue };
// varargs
public static void printf(String fmt, Object[] args...) {
int i = 0;
// foreach on primitive array
for (char c : fmt.toCharArray()) {
if (c == '%')
System.out.print(args[i++]);
else
System.out.print(c);
}
}
public static void main(String[] args) {
// Integer list
LinkedList<Integer> xs = new LinkedList<Integer>();
xs.add(new Integer(0)); xs.add(new Integer(1));
Integer x = xs.iterator().next();
Integer mb = Collections.max(xs);
// string list
LinkedList<String> ys = new LinkedList<String>();
ys.add("zero"); ys.add("one");
String y = ys.iterator().next();
// string list list
LinkedList<LinkedList<String>> zss = new LinkedList<LinkedList<String>>();
zss.add(ys);
String z = zss.iterator().next().iterator().next();
// foreach on a collection
for (String s : ys)
System.out.println(s);
// varargs and boxing
printf("Addition: % plus % equals %\n", 1, 1, 2);
// use static import
printf("sin(PI/12) = %\n", sin(PI/12));
// use enums
printf("Colors are %\n", Color.VALUES);
for ( Color c : Color.VALUES ) {
// switch on enum
switch(c) {
case Color.red:
System.out.println("found red.");
break;
case Color.green:
System.out.println("found green.");
break;
case Color.blue:
System.out.println("found blue.");
break;
}
}
}
}
Here it is again with the author's proposed syntax:
import java.util.LinkedList;
import java.util.Collections;
import static java.lang.Math.*; // import static
class Test {
// enum
enum Color { red, green, blue };
// varargs
public static void printf(String fmt, Object[] args...) {
int i = 0;
// foreach on primitive array
for (char c = eachof fmt.toCharArray()) {
if (c == '%')
System.out.print(args[i++]);
else
System.out.print(c);
}
}
public static void main(String[] args) {
// Integer list
(Integer) LinkedList xs = new LinkedList;
xs.add(new Integer(0)); xs.add(new Integer(1));
Integer x = xs.iterator().next();
Integer mb = Collections.max(xs);
// string list
(String) LinkedList ys = new LinkedList;
ys.add("zero"); ys.add("one");
String y = ys.iterator().next();
// string list list
((String) LinkedList) LinkedList zss = new LinkedList;
zss.add(ys);
String z = zss.iterator().next().iterator().next();
// foreach on a collection
for (String s = eachof ys)
System.out.println(s);
// varargs and boxing
printf("Addition: % plus % equals %\n", 1, 1, 2);
// use static import
printf("sin(PI/12) = %\n", sin(PI/12));
// use enums
printf("Colors are %\n", Color.VALUES);
for ( Color c = eachof Color.VALUES ) {
// switch on enum
switch(c) {
case Color.red:
System.out.println("found red.");
break;
case Color.green:
System.out.println("found green.");
break;
case Color.blue:
System.out.println("found blue.");
break;
}
}
}
}
References
"JSR 14: Add Generic Types To The Java Programming Language"
"Prototype for JSR014: Adding Generics to the Java Programming Language v. 2.2"
"GJ: Extending the Java Programming Language with type parameters"
Stephen Jungels is a computer consultant with over five years of experience, currently specializing in Web applications development.
Return to ONJava.com.
-
This article is in the way of useful information
2004-01-20 08:57:12 anonymous2 [View]
-
Generics syntax: why not make it obvious
2004-01-02 10:36:45 ggregory [View]
- Trackback from http://www.nathanjones.com/counterfactual/archives/000164.html
Syntax, semantics and sex. Well, not really.
2003-11-21 19:44:43 [View]
-
c.size() == d.size() == e.size()
2003-10-22 14:01:14 anonymous2 [View]
-
Reduce syntax, do not expand it
2003-10-17 06:58:57 anonymous2 [View]
-
Reduce syntax, do not expand it
2003-10-17 06:49:46 anonymous2 [View]
-
More links
2003-09-28 13:39:09 sjungels [View]
-
eachof
2003-09-27 19:46:50 anonymous2 [View]
-
http://www.codon4.com
2003-09-27 08:06:26 anonymous2 [View]
-
Thought-Thru Goodness
2003-09-26 14:51:51 anonymous2 [View]
-
JSR Feedback
2003-09-26 11:18:00 anonymous2 [View]
-
Generic Syntax
2003-09-25 18:47:55 anonymous2 [View]
-
Readable Java 1.5
2003-09-25 15:42:48 anonymous2 [View]
-
Interesting Ideas but...
2003-09-25 06:48:49 anonymous2 [View]
-
Cholesterol
2003-09-25 06:35:52 anonymous2 [View]
-
interesting, but...
2003-09-25 05:36:07 anonymous2 [View]
-
I partially agree
2003-09-25 00:38:22 anonymous2 [View]
-
I like Sun's 1.5 ideas better
2003-09-24 21:12:02 anonymous2 [View]
-
I like this
2003-09-24 16:39:46 revdiablo [View]