Sunday, 19 August 2012

Java: lambda support in Java 8 (pre-release)

Pre-release builds of Java 8 with lambda support are available for download. This post looks at lambda-8-b50-linux-x64-26_jul_2012.tar.gz.

Java 7

Consider this code for checking whether an integer is a prime number:

public final class Primes {
  public static boolean isPrime(int candidate) {
    if (candidate % 2 == 0) {
      return candidate == 2;
    }
    for (int i = 3, root = (int) Math.sqrt(candidate) + 1; i < root; i += 2) {
      if (candidate % i == 0) {
        return false;
      }
    }
    return candidate >= 3;
  }
}

Let's use it to find the prime numbers in a set of integers:

import java.util.Set;
import com.google.common.base.Predicate;
import com.google.common.collect.Sets;

public class Find {
  public static Set<Integer> primes(Set<Integer> set) {
    Predicate<Integer> predicate = new Predicate<Integer>() {
      @Override
      public boolean apply(Integer n) {
        return Primes.isPrime(n);
      }
    };

    return Sets.filter(set, predicate);
  }
}

This code uses the Google Guava library for the filtering; we need only provide a predicate for the filter.

Here is the JUnit unit test:

import static com.google.common.collect.Sets.newHashSet;
import static com.google.common.collect.Sets.union;
import static org.junit.Assert.assertEquals;
import java.util.Set;
import org.junit.Test;

public class Java7Test {
  @Test
  public void testPrimes() {
    Set<Integer> primes = newHashSet(2, 3, 5, 6373, 6379, 6389, 6397, 6421,
        6427, 6449, 6451, 6469, 6473, 7919);
    Set<Integer> nonprime = newHashSet(0, 1, 10, 99, 1000);
    Set<Integer> union = union(primes, nonprime);
    Set<Integer> filtered = Find.primes(union);
    assertEquals(primes, filtered);
  }
}

Java 8

The Predicate type can be considered a functional interface by the definition stated in JSR 335: Lambda Expressions for the Java Programming Language*:

A functional interface is an interface that has just one abstract method, and thus represents a single function contract. (In some cases, this "single" method may take the form of multiple abstract methods with override-equivalent signatures inherited from superinterfaces; in this case, the inherited methods logically represent a single method.)

*version 0.5.1

With lambda support implementing one-method interfaces becomes more succinct.

Implementing the predicate with a lambda expression:

  public static Set<Integer> primes(Set<Integer> set) {
    Predicate<Integer> predicate = (n) -> {
      return Primes.isPrime(n);
    }; 
    return Sets.filter(set, predicate);
  }

Single-expression lambda:

  public static Set<Integer> primes(Set<Integer> set) {
    Predicate<Integer> predicate = (n) -> Primes.isPrime(n);
    return Sets.filter(set, predicate);
  }

Inline expression:

  public static Set<Integer> primes(Set<Integer> set) {
    return Sets.filter(set, (n) -> Primes.isPrime(n));
  }

Replacing the expression with a method reference:

  public static Set<Integer> primes(Set<Integer> set) {
    return Sets.filter(set, Primes::isPrime);
  }

With each change the primes method tends towards making itself redundant.

Building

To my surprise, this build worked with Maven (3.0.4) out of the box. Here's my pom.xml:

<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
  <modelVersion>4.0.0</modelVersion>
  <groupId>demo</groupId>
  <artifactId>lambda</artifactId>
  <version>0.0.1-SNAPSHOT</version>
  <build>
    <plugins>
      <plugin>
        <groupId>org.apache.maven.plugins</groupId>
        <artifactId>maven-compiler-plugin</artifactId>
        <version>2.3.2</version>
        <configuration>
          <source>1.8</source>
          <target>1.8</target>
        </configuration>
      </plugin>
    </plugins>
  </build>
  <dependencies>
    <dependency>
      <groupId>com.google.guava</groupId>
      <artifactId>guava</artifactId>
      <version>12.0</version>
    </dependency>
    <dependency>
      <groupId>junit</groupId>
      <artifactId>junit</artifactId>
      <version>4.10</version>
      <scope>test</scope>
    </dependency>
  </dependencies>
</project>

3 comments:

  1. Hi,
    Thanks for your post.
    I just can't set maven to compile on Java 8, using the same pom you presented, even if default java system version is actually java 8.
    The selected JRE environment is the default of Maven which is 1.4.

    Did you manage to configure something else ?

    For info, i'm on eclipse Luna and Ubuntu 13.01.

    Thanks in advance for your feedback.

    ReplyDelete
    Replies
    1. At the time I did not use an IDE - just the latest command-line Maven with the JDK and a text editor. If memory serves I set the JAVA_HOME, M2_HOME and updated the PATH environment variables in my .profile file.

      Since Luna isn't due out until next year I don't know the state of its Java 8 support or what stage the M2E Maven plugin is at. For a more recent post on Java 8 I used IntelliJ IDEA.

      Note that this post is over a year old and refers to an obsolete build.

      Delete
    2. Thanks for your quick answer.
      Using command line Maven, I also manage to compile my java 8 project.
      No chance to get it working with Eclipse Luna which should have a Java 8 support.

      Probably to best bet would be using IntelliJ

      Delete

All comments are moderated