Skip to content
peichhorn edited this page Jun 30, 2012 · 5 revisions

@Function

Overview

This annotation allows for a class to be automatically wrapped as an anonymous class, allowing the caller to pass it as an argument to a method that accepts a 'function'.

Encapsulates a method that returns a value, so its signature looks like this:

public R methodName(T1 t1, T2 t2, ..., Tn tn) 

Example

With Lombok

import lombok.ExtensionMethod;
import lombok.Function;
import java.util.Arrays;
import lombok.Functions.Function1;
import static lombok.Yield.yield;

@ExtensionMethod(Operations.class)
public class FunctionExample {

  public static void main(String[] args) {
    new String[] { "john", "james", "eddie" }.toList().filter(startsWith("j")).map(toUpperCase()).print('-');
    // outputs "JOHN-JAMES"
  }

  @Function
  private static boolean startsWith(String element, String _prefix) {
    return element.startsWith(_prefix);
  }

  @Function
  private static String toUpperCase(String element) {
    return element.toUpperCase();
  }
}

public class Operations {

  public static <T> Iterable<T> filter(final Iterable<T> list, final Function1<T, Boolean> filter) {
    for (T element : list) if (filter.apply(element)) yield(element);
  }

  public static <S, T> Iterable<T> map(final Iterable<S> list, final Function1<S, T> mapping) {
    for (S element : list) yield(mapping.apply(element));
  }

  public static <T> Iterable<T> toList(final T[] array) {
    return Arrays.asList(array);
  }

  public static <T> void print(final Iterable<T> list, final char delimiter) {
    StringBuilder sb = new StringBuilder();
    for (T element : list) {
      sb.append(element.toString()).append(delimiter);
    }
    System.out.println(sb.length() > 0 ? sb.substring(0, sb.length() - 1) : "");
  }
}

Vanilla Java

import java.util.Arrays;
import lombok.Functions.Function1;

public class Main {

  public static void main(String[] args) {

    Operations.print(Operations.map(Operations.filter(Operations.toList(new String[]{"john", "james", "eddie"}), artsWith("j")), toUpperCase()), '-');
    // outputs "JOHN-JAMES"
  }

  @java.lang.SuppressWarnings("all")
  private static lombok.Functions.Function1<String, java.lang.Boolean> startsWith(final String _prefix) {
    return new lombok.Functions.Function1<String, java.lang.Boolean>(){
      
      public java.lang.Boolean apply(final String element) {
        return element.startsWith(_prefix);
      }
    };
  }

  @java.lang.SuppressWarnings("all")
  private static lombok.Functions.Function1<String, String> toUpperCase() {
    return new lombok.Functions.Function1<String, String>(){
      
      public String apply(final String element) {
        return element.toUpperCase();
      }
    };
  }
}

public class Operations {
  // same as above
  // see also Yield.yield() - https://github.com/peichhorn/lombok-pg/wiki/Yield
  // see also @ExtensionMethod - https://github.com/peichhorn/lombok-pg/wiki/%40ExtensionMethod
}

Behind the Scenes

By default @Function uses lombok.Functions as template class. This means you need some lombok-pg classes at runtime. These runtime dependencies are located in lombok-pgs runtime artefact called lombok-pg-runtime.jar. As a benefit of using lombok.Functions you get type information for free, which is accessible via getter methods such as: Class<?> getReturnType(), Class<?> getParameterType1() ... Class<?> getParameterTypeN() on the function objects.

You may pass functions to other functions, creating higher order functions and monads. Furthermore closures are possible, just prefix a function's argument name with an underscore (_) to specify, as seen in the example above.

Configuration

If you want to avoid having a runtime dependency to lombok-pg or just want to define you own template, use @Function(template=MyFunctionTemplate.class) and make sure that MyFunctionTemplate satisfies the following conditions:

  • The template class and any accessible (meaning public static) inner class qualifies as a possible template if they are abstract classes or interfaces that have only one abstract public method.
  • The return type and the type arguments of the abstract method have to match the type arguments of the surrounding type.
  • A template class must not define multiple templates for the same argument count.

Template Example: lombok.Functions (source)

Being able to specify a template class also allows you to use @Function with other libraries such as google-guava @Function(template=com.google.common.base.Function.class).