Skip to content

Lambdas

Java is an imperative, object-oriented programming language and not a functional programming language. But since version 8 some aspects of functional programming are supported in Java.

Functions are similar to methods. But unlike methods, functions are passed on in functional programming, for example as arguments to methods or as the return value of a method. This is sometimes referred to as code-as-data.

Unlike functional languages, Java does not offer function types. Nevertheless, references to functions can be passed on.

With so-called "currying" functions can also be manipulated.

Pure functional languages such as Haskell are stateless and functions have no side effects. You do not change any data, you can at most return new data. This is different with Java. Functions can change data here.

Purely functional programming is ideally suited for parallel processing as well as mathematical tasks, and has the reputation that programs created in this way should contain fewer errors. The disadvantage is that real objects and status changes can only be mapped with difficulty, which makes programming real operations and business processes more difficult. This is much easier to achieve with object-oriented stateful programming.

Lambda expressions provide a clear and concise way to implement a single-method interface using an expression. They allow you to reduce the amount of code you need to create and maintain. Although they are similar to anonymous classes, they do not have any kind of information on their own. Method References implement functional interfaces using existing methods instead of expressions. They also belong to the Lambda family.

However, the java is not a funtional programming language, but from the version 1.0, it contains a simple form of function definition as you see in the following example:

class StringLengthComparator implements Comparator {
    private StringLengthComparator() { }
    public static final StringLengthComparator INSTANCE =
            new StringLengthComparator();
    public int compare(Object o1, Object o2) {
        String s1 = (String) o1, s2 = (String) o2;
        return s1.length() - s2.length();
    }
}
Arrays.sort(words, StringLengthComparator.INSTANCE);

The above code in Java 5, looks like this:

Arrays.sort(words, new Comparator<String>() {
    public int compare(String s1, String s2) {
        return s1.length() - s2.length();
    }});

And in Java 8, it is:

Arrays.sort(words, (s1, s2) -> s1.length() - s2.length());

Functional Interface Types

Interfaces with a single abstract method are now called Functional Interface Types or SAM Types. SAM stands for Single Abstract Method. Such interfaces can be provided with the @FunctionalInterface annotation and have a special meaning for lambdas, since they are the only types that can be used as "target typing target types".

The examples of SAM Types are Predicate, Function, Consumer and all other functional interfaces in java.util.function, as well as ActionListener, AutoCloseable, Callable, Comparable, Comparator, FileFilter, FilenameFilter, Iterable, Runnable, etc.

Lambda expressions

Lambda expressions (according to JSR335) represent functions without names, without explicit specification of the return type and without declaration of exceptions.

The general syntax for a lambda expression is:

(Parameter list) -> {expression or statements}

Syntax Example
parameter -> expression x -> x * x
parameter -> block s ->
(parameters) -> expression (x, y) -> Math.sqrt(xx + yy)
(parameters) -> block (s1, s2) ->
(parameter decls) -> expression (double x, double y) -> Math.sqrt(xx + yy)
(parameters decls) -> block (List<?> list) ->

For example:

(File f) -> {return f.isFile (); }

However, there are different abbreviations. To demonstrate different notations for lambda expressions, the following code snippets all fulfill the same functionality: In the directory "/MyDirectory" all files, but no subdirectories, are listed with the method File.listFiles (FileFilter). The variable myDir is defined by:

File myDir = new File ("/MyDirectory");
Without lambda: Conventional programming of the filter function as an anonymous class.

File [] files = myDir.listFiles (
    new FileFilter () {
      @Override
      public boolean accept (File f) {
         return f.isFile ();
      }
   }
);

With lambda: assignment of the filter function as a lambda expression (File f) -> {return f.isFile ();} to the SAM interface FileFilter and transfer of the filter function MeineFilterFfunktion defined in this way as method parameters to listFiles().

FileFilter myFilterFunction = (File f) -> {return f.isFile (); };  
File [] files = myDir.listFiles (myFilterFunction); 

With lambda as a method parameter: Direct transfer of the lambda expression as a method parameter.

File [] files = myDir.listFiles ((File f) -> {return f.isFile ();}); 

With lambda - Abbreviated notation: Abbreviated notation without type specification and without brackets. The type specification can be omitted if the compiler can "deduce" it from the context. If there is only one parameter without a type specification, the left round brackets can be omitted. If there is no instruction on the right, but only a single expression, the curly brackets on the right are omitted.

File [] files = myDir.listFiles (f -> f.isFile ()); 

With lambda - even shorter: Even shorter notation with the "method reference" "File :: isFile", which has been possible since Java 8. A lambda can be replaced by a method reference (or constructor reference, see below) if no further action is required besides the method call.

File [] files = myDir.listFiles (File :: isFile); 

Further examples of lambda expressions:

Code snippet explanation
(int x, int y) -> x + y Several parameters can be passed.
(x, y) -> x + y The type specifications can also be omitted if there are several parameters.
() -> "blupp" An empty parameter list is also allowed.
(x, y) -> (x> y)? x: y The?: operator counts as a single expression and the right curly brackets are omitted.
(x, y) -> Curly brackets must be used for instructions.
Object obj1 = (FileFilter) f -> f.isFile (); Object obj2 = (FileFilter) File :: isFile; Lambda expressions can only be assigned to SAM interfaces. But this example shows how indirect assignments to other types are possible.

Method references

Method references is a more succinct alternative to lambdas, it can be defined using one of the follwoing ways:

  • An instance method of a particular object (bound)
    • objectRef::methodName
  • An instance method whose receiver is unspecified (unbound)
    • ClassName::instanceMethodName
    • The resulting function has an extra argument for the receiver
  • A static method
    • ClassName::staticMethodName
  • A constructor
    • ClassName::new

Method references Exmaple

Kind Examples
Bound instance method System.out::println
Unbound instance method String::length
Static method Math::cos
Constructor LinkedHashSet::new
Array constructor String[]::new

Exercise

  1. Exercise:
    • Calculate sum of an integer list with reduce()
  2. Exercise:
    • Calculate sum of an integer list with sum()
  3. Exercise:
    • Filter all values which are Positive in X with filter()
  4. Exercise:
    • Filter distinct values which are Positive in X with filter()