2014-07-17

Java 8 Functional Interface Naming Guide

People have been complaining about the naming of Java 8's functional interfaces in the java.util.function package. Depending on the types of arguments and return values a function may instead be called a consumer, supplier, predicate or operator, which is further complicated by two-argument functions. The number of interfaces explodes because of specialized interfaces for some primitive types. In total there are 43 interfaces in that package. Compare this to Scala where just three interfaces (with compiler optimizations) cover all the same use cases: Function0, Function1 and Function2.

To make some sense out of this all, I wrote a program that generates the interface names based on the function's argument types and return type. The names it generates match those in the Java library. For this article I also made visualization which you can see below.

Visually

Click to make it bigger.

Textually

The interface name is determined by the following algorithm. The names are described as regular expressions; the generic interfaces have the shortest names, but specialized interfaces for functions taking or returning primitives include the primitive types in the interface name.

  1. Does it return void?
    • Arity 0: Runnable
    • Arity 1: (|Int|Long|Double)Consumer
    • Arity 2:
      • Both arguments are generic: BiConsumer
      • First argument is generic: Obj(Int|Long|Double)Consumer
  2. Does it take no arguments?
    • Arity 0: (|Int|Long|Double|Boolean)Supplier
  3. Does it return boolean?
    • Arity 1: (|Int|Long|Double)Predicate
    • Arity 2: BiPredicate
  4. Do all arguments have the same type as the return value?
    • Arity 1: (|Int|Long|Double)UnaryOperator
    • Arity 2: (|Int|Long|Double)BinaryOperator
  5. Otherwise:
    • Arity 1: (|Int|Long|Double)(|ToInt|ToLong|ToDouble)Function
    • Arity 2: (|ToInt|ToLong|ToDouble)Function

The method names follow. When the return type is a primitive, the method name is a bit longer (this is apparently because the JVM supports method overloading also based on the method return type, but the Java language does not).

  • Runnable: run
  • Consumers: accept
  • Suppliers: get(|AsInt|AsLong|AsDouble|AsBoolean)
  • Predicates: test
  • Functions and operators: apply(|AsInt|AsLong|AsDouble)

I hope that helps some of you in remembering what interface to look for when browsing the API.

No comments: