Immutables

Functions and Predicates

Overview

Java 8 provides lambda expressions, which are very handy for functional programming:

people.stream()
      .map(p -> p.name())

They are particularly useful for filtering and transforming immutable value objects, but many programmers, due to customer requirements or other reasons, cannot use Java 8. Guava provides many of the functional capabilities of Java 8 using their Function and Predicate interfaces:

class PersonNameFunction implements Function<Person, String> {
  @Override
  public String apply(Person person) {
    return person.name();
  }
}

ImmutableList<String> names = FluentIterables.from(people)
    .transform(new PersonNameFunction());

However, without lambdas, writing Functions and Predicates is verbose and often results in functional code less clear than its imperative equivalent.

Generate functions and predicates

With org.immutables:func, you can easily generate Guava Functions and Predicates for field access without the clutter. Special class *Functions is generated and provides function and predicate instances.

@Value.Immutable
@Functional
abstract class Person {
  public abstract String name();
  public abstract String jobTitle();
  public abstract boolean speaksFrench();
}

List<String> names = Lists.transform(people, PersonFunctions.name());
// Boolean attributes become `Predicates`
Iterable<Person> frenchSpeakers = Iterables.filter(people, PersonFunctions.speaksFrench());

By placing @Functional on a method instead of the class, you can restrict which Functions and Predicates are generated:

@Value.Immutable
abstract class Person {
  @Functional
  public abstract String name();
  // no Function will be generated for jobTitle
  public abstract String jobTitle();
  @Functional
  public abstract boolean speaksFrench();
}

Of course, you can use static imports to further reduce clutter.

import ...PersonFunctions.*;
import ...FluentIterable.*;

List<String> frenchSpeakerNames =
    from(people)
        .filter(speaksFrench())
        .transform(name())
        .toList();

Generate functions with bound parameters

@Functional.BindParameters annotation can be place on non-accessor methods of abstract value type to generate function to which parameters can be bound.

@Value.Immutable
public abstract class Xyz {
  @Functional
  public abstract String getX();

  @Functional.BindParameters
  public String computeZ(String y) {
    return getX() + y;
  }
}
...
// Generated functions
public final class XyzFunctions {
  ...
  public static Function<Xyz, String> computeZ(String y) {
    return new Function<Xyz, String>() {
      @Override
      public String apply(Xyz input) {
        return input.computeZ(y);
      }
      @Override
      public String toString() {
        return "XyzFunctions.computeZ(y)";
      }
    };
  }
}
...
// use as
Function<Xyz, String> fn = XyzFunctions.computeZ("Y");

Dependencies

This feature has the following compile-time dependencies in addition to a runtime dependency on Guava:

<dependency>
  <groupId>org.immutables</groupId>
  <artifactId>value</artifactId>
  <version>2.10.1</version>
  <scope>provided</scope>
</dependency>
<dependency>
  <groupId>org.immutables</groupId>
  <artifactId>func</artifactId>
  <version>2.10.1</version>
  <scope>provided</scope>
</dependency>