Functional Programming in Java 8 with Lambda Expressions

Lambda expressions are the most important new feature driving the Java 8 release. They let you pass a function as a parameter to a method (a function passed into a method). So you must upgrade to JDK 8 or above to use Lambda expressions; if you're on JDK 7, you're out of luck.

How It Started

If you don’t like the story, skip to the next section.

It started when I planned to integrate Redis caching. After configuring Redis, I found the @Cacheable annotation never took effect. After half a day of struggle my spirit was broken, so I decided to drop @Cacheable and handle caching and reading myself. Of course the topic of this article is Lambda expressions, so I’ll gloss over the caching issue. When getting and setting the cache myself, I found lots of duplicated code — the same if checks — so I decided to write a generic method to remove the duplication, and thought of functional programming: Lambda expressions can slim down the code. I’m sharing it as a reference.

What Is a Lambda Expression

Lambda expressions are the most important new feature driving the Java 8 release. They let you pass a function as a parameter to a method (a function passed into a method). So you must upgrade to JDK 8 or above to use Lambda expressions; if you’re on JDK 7, you’re out of luck.

Lambda Expression Syntax

(parameters) -> expression or (parameters) ->{ statements; }

The parameter types in the parentheses can be omitted; the compiler infers them uniformly. The braces can be omitted when there’s only a single statement.

Getting Started with Lambda Expressions

Step 1: first define a functional interface, e.g.

@FunctionalInterface
public interface IFunctionObject {
    Object function();
}

A functional interface is, first, an interface, and it may contain only one abstract method. It’s also called a SAM (Single Abstract Method) interface. The @FunctionalInterface annotation is a compile-time check — if the interface isn’t a functional interface, the compiler errors out; it’s an error-checking aid.

Step 2: call the functional interface in your business logic, e.g.

/**
 * Get an object from cache; if absent, run the object-fetching interface and put it in cache
 *
 * @param key            H
 * @param hashKey        HK
 * @param functionObject object-fetching interface
 * @return object
 */
protected Object cacheGet(String key, String hashKey, IFunctionObject functionObject) {
    Object object = null;
    try {
        // get object from cache
        object = redisTemplate.opsForHash().get(key, hashKey);
        if (object == null) {
            // object not found; run the object-fetching interface and put it in cache
            object = functionObject.function();
            if (object != null && object instanceof Serializable)
                cacheSetHash(key, hashKey, object);
        }
    } catch (Exception e) {
        log.error(e.getMessage(), e);
    }
    return object;
}

Step 3: call our business code with a Lambda expression, e.g.:

/**
 * Get article by ID
 *
 * @param id article ID
 * @return article
 */
public Article getArticleById(Long id) {
    // get from cache first; if absent, get from database
    Article article = (Article) cacheGet("article", id.toString(), () -> articleMapper.selectByPrimaryKey(id));
    // after retrieval, increment view count
    if (article != null) {
        setViewAdd(id);
    }
    return article;
}

Other examples of functional interfaces you can call with Lambda expressions: all of the following can be invoked with a Lambda expression

java.lang.Runnable,

java.awt.event.ActionListener,

java.util.Comparator,

java.util.concurrent.Callable

Interfaces under the java.util.function package, such as Consumer, Predicate, Supplier, etc.

Summary

Here’s my understanding: a Lambda expression is essentially an anonymous function, and that function can be passed as a parameter between methods. You can think of a functional interface as a Class type, and the Lambda expression as a variable of that type — except this variable is special: it can run some logic and then return a result.