A DESCRIPTION OF THE REQUEST :
The java.lang.Runnable interface provides a common definition of code-as-data accross Java libraries and applications. This interface is limited, however, to functions that consume no input and produce no output. By introducing type parameters, Java 5.0 introduces the possibility of a wide range of variations. A more general framework (defined in java.lang or elsewhere) would include the following:
- Thunk<T>, a Runnable that produces a return value. (java.util.concurrent.Callable is this interface, although concurrency is just one of many domains in which a thunk would be useful)
- Lambda<T, R> (alternatively, Function<T, R>), a function that takes an argument and produces a return value.
- Predicate<T>, a Lambda with a boolean return type (this could either extend Lambda<T, Boolean> or simply use the boolean primitive type)
- Lambda2<T1, T2, R>, Runnable1<T>, Runnable2<T1, T2>, Predicate2<T1, T2>, etc. Members of the code-as-data family that accept multiple arguments. (For an example from the Java APIs, see java.awt.event.ActionListener.) The language does not allow the definition of such interfaces that take an *arbitrary* number of arguments, but a reasonable number (say, 3 or 4), could be handled with separate classes.
The treatment of code as data is a fundamental property of object-oriented languages. However, the treatement in the Java APIs does not reflect this fact. The usefulness of first-class functions has been demonstrated extensively, especially in the context of functional languages. While Java is not Scheme, and first-class functions must be "simulated" by wrapping them in an object, closures on anonymous inner classes allow algorithms based on first-class functions to be easily expressed in Java as long as the necessary interface definitions are available.
While the definition of these interfaces can be easily done on-the-fly by developers as their applications require, the benifits of having these definitions in the standard API would include:
- A globally agreed-upon interface for exchanging code-as-data accross library boundaries
- A consistent naming scheme, improving code readability (it's easier to understand that a Widget takes a Runnable as input than it is to figure out that a WidgetRunAction is just yet another implementation of Runnable with a different name)
- The opportunity to define sophisticated utility methods and classes in the API to manipulate objects of these types, such as composition, currying, lazy evaluation, etc.
- The opportunity to similarly define methods in the API that act on arrays, collections, and iterables in terms of these objects. For example, the map() and filter() methods could apply a Lambda and a Predicate, respectively, to a collection.
- The opportunity for developers to define sophisticated methods that operate on objects of these types and then use implementations from the APIs or other libraries.
- Consistency within the API could be achieved by bringing various code-as-data implementations (Comparable, ActionListenever, and Callable are examples) under the same umbrella. (This would be limited by that fact that these implementations use different method names, and while classes can simply call the old method from the new, uniform method, it would be unreasonable to add an extra uniform method name to an interface)
CUSTOMER SUBMITTED WORKAROUND :
An open-source definition of these interfaces, some general-purpose implementations, and utility methods for objects of these types, can be found here (as used by the DrJava project):
Javadocs can be found here: