Search

Categories

Links

Referers

Considered Harmful

Jul 27 2005, 10:44:51 PM PDT »Java Comments [16]
Dijkstra wrote a classical paper Go To Statement Considered Harmful. A paper that was considered classical in 1995 is truly a classic in our business ;-)

Dijkstra did understand what he was talking about.

Dijkstra was urging the community to move forward and work with higher abstractions.

Dijkstra points out that our intellectual powers are limited in a fashion that should make us strive to shorten the conceptual gap between the static program and the dynamic process.

A modern example of this conceptual gap is found when adding an element to a collection and extracting it in the Java™ Programming Language before the advent of generic types:

List strings = new ArrayList(); // list of strings
strings.add("Hello");
...
String s = (String)strings.get(0);

Notice how the programmer is required to bridge a conceptual gap by adding comments and casts that aren't enforced by the compiler as it is with generic types:

List<String> strings = new ArrayList<String>();
strings.add("Hello");
...
String s = strings.get(0);

Also notice that the compiler will notify the developer if he makes mistakes:

Integer i = (Integer)strings.get(0); // compile-time error
strings.add(1); // compile-time error

The benefits should be clear and I like to think if Dijkstra had been consulted on generics he would have said: "not having generic types should be considered harmful".

Ken Arnold doesn't understand generics and thus he suggest that generics should be considered harmful. Let's examine Ken's arguments:

My colleague told me something I misunderstood, but when I follow my wrong conclusions to the end, it doesn't make sense. Thus generics is considered harmful. And so Ken declares that you shouldn't do this:

    interface Holder<T> {
        T[] toArray();
    }

Allow me to correct your misunderstanding. You can't do this:

class Box<T> {
    T[] array = new T[10]; // compile-time error
}

You should not try to work around it:

class Box<T> {
    T[] array = (T[])new Object[10]; // BAD and causes a warning
}

This is an unfortunate side-effect of erasure, the VM doesn't know what T is at runtime, so the compiler has no way to generate code that can construct an array of the right type. In the VM, all arrays must record their element type. This is because arrays should be considered harmful:

public class Test {
    public static void main(String... args) {
        Object[] array = new String[10];
        array[0] = new Object(); // causes a run-time error, ArrayStoreException
    }
}
The "intuitively correct" type rule that allows the first assignment demonstrates that arrays aren't statically type safe. However, they are type safe: the VM eventually rejects the problematic assignment.

If you replace arrays with the generic ArrayList, you get this program:

import java.util.*;
public class Test {
    public static void main(String... args) {
        List<Object> array = new ArrayList<String>(10); // compile-time error
        array.add(0, new Object());
    }
}

So Ken and others should consider arrays harmful, not generics.

Ken also says that because he and David can't understand the declaration of Enum then it must be evil.

Let's look at Enum, it's not that complicated:

class Enum<E extends Enum<E>> { ... }

This is called an F-bound, I'm not exactly sure what the F means but think of it as function bounds. The bound on the type variable is a function (F) of the type variable. Yes, I know that probably didn't help your understanding. Hold on, here goes...

Since Enum is a special implementation class used for the new enum types, let's look at Comparable instead. The definition of Collections.sort looks like this:

public static <T extends Comparable<? super T>> void sort(List<T> list)

T should be a type which implements Comparable of a supertype of T. In other words: T must be comparable to it self (or a supertype of itself). This is a simple recursive definition.

So how do you validate that a given type meets the criteria (is within the bounds of T). It's simple: substitute T for the type and check if the statement is true. For example, Integer implements Comparable<Integer>, so the question is does

Integer extend Comparable<? super Integer>

The answer is yes. Integer implements Comparable<Integer> which is a subtype of Comparable<? super Integer>. Piece of cake.

Java is a trademark of Sun Microsystems, Inc.
Copyright © 2006,2007 Peter von der Ahé