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

advertisement

AddThis Social Bookmark Button

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 as List<+Number>.
  • A contravariant, or write-only list of Numbers as List<-Number>.
  • A bivariant, read-write list of Numbers as List<*>.
  • An invariant list of Numbers as List<=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

Stephen Jungels is a computer consultant with over five years of experience, currently specializing in Web applications development.


Return to ONJava.com.