6.005 — Software Construction on MIT OpenCourseWare | OCW 6.005 Homepage
Spring 2016

Reading 19, Sidebar: Anonymous Runnable

Anonymous classes

Usually when we implement an interface, we do so by declaring a class. For example, given the interface Comparator in the Java API:

/** A comparison function that imposes a total ordering on some objects.
 *  ... */
public interface Comparator<T> {
    /** Compares its two arguments for order.
     *  ...
     *  @return a negative integer, zero, or a positive integer if the first
     *          argument is less than, equal to, or greater than the second */
    public int compare(T o1, T o2);
}

We might declare:

/** Orders Strings by length (shorter first) and then lexicographically. */
public class StringLengthComparator implements Comparator<String> {
    @Override public int compare(String s1, String s2) {
        if (s1.length() == s1.length()) {
            return s1.compareTo(s2);
        }
        return s1.length() - s2.length();
    }
}

One purpose of Comparator is for sorting. A SortedSet keeps its items in a total order.

Without a Comparator , the SortedSet implementation uses the compareTo method provided by the objects in the set:

SortedSet<String> strings = new TreeSet<>();
strings.addAll(Arrays.asList("yolanda", "zach", "alice", "bob"));
// strings is { "alice", "bob", "yolanda", "zach" }

With a Comparator :

// uses StringLengthComparator declared above
Comparator<String> compareByLength = new StringLengthComparator();
SortedSet<String> strings = new TreeSet<>(compareByLength);
strings.addAll(Arrays.asList("yolanda", "zach", "alice", "bob"));
// strings is { "bob", "zach", "alice", "yolanda" }

If we only intend to use this comparator in this one place, we already know how to eliminate the variable:

// uses StringLengthComparator declared above
SortedSet<String> strings = new TreeSet<>(new StringLengthComparator());
strings.addAll(Arrays.asList("yolanda", "zach", "alice", "bob"));
// strings is { "bob", "zach", "alice", "yolanda" }

An anonymous class declares an unnamed class that implements an interface and immediately creates the one and only instance of that class. Compare to the code above:

// no StringLengthComparator class!
SortedSet<String> strings = new TreeSet<>(new Comparator<String>() {
    @Override public int compare(String s1, String s2) {
        if (s1.length() == s1.length()) {
            return s1.compareTo(s2);
        }
        return s1.length() - s2.length();
    }
});
strings.addAll(Arrays.asList("yolanda", "zach", "alice", "bob"));
// strings is { "bob", "zach", "alice", "yolanda" }

Pros:

  • If we’re only using the comparator in this one piece of code, we’ve reduced its scope. Previously, any other code could start using and depending on StringLengthComparator .

  • A reader no longer has to search elsewhere for the details of the comparator, everything is right here.

Cons:

  • If we need the same comparator more than once, we might be tempted to copy-and-paste. The old way is DRY.

  • If the implementation of the comparator is long, it interrupts the surrounding code, making it harder to understand. The old way is broken into modular pieces.

So anonymous classes are good for short one-off implementations of a method.

Using an anonymous Runnable to start a thread

The Runnables we use to create new threads often meet these criteria perfectly.

Here’s the example from the reading :

public static void main(String[] args) {
    new Thread(new Runnable() {
        public void run() {
            System.out.println("Hello from a thread!");
        }
    }).start();
}

Rather than (1) declare a class that implements Runnable where the run method calls System.out.println , (2) create an instance of that class, and (3) pass that instance to the Thread constructor, we do all three steps in one go with an anonymous Runnable .


If you’re feeling clever, you can go one step further with Java’s lambda expressions :

public static void main(String[] args) {
    new Thread(() -> System.out.println("Hello from a thread!")).start();
}

Whether that’s more or less easy to understand is up for debate. Runnable and run never appear at all, so you certainly have to do more research to understand this construction the first time you come across it.

Back to Concurrency