Java 8 – The Road to Lambda expressions

Lucas Jellema 1
0 0
Read Time:5 Minute, 5 Second

image

Java 8 offers wonderful opportunities and new interesting intellectual challenges for Java developers. With this upcoming release, we are once again focused on programming itself – instead of yet another framework – and learning new programming concepts. The introduction of Generics was the last time Java programming was significantly changed – quite some time ago – and this time this change is both more fundamental and more rewarding. Lambda expressions allow for new, compact, elegant ways of programming that bring the notion of Inversion of Control to the core Java programming language. The language itself as well as APIs created with it can make the distinction between the what [should be done] and the how [should be it done]. The what is expressed in a Lambda expression – a function that can be passed around as a parameter – and the how is left to the API implementation or indeed the JVM itself. A great example of this distinction is found in the parallel execution supported in the Collection classes. Another good example is the tremendous elegance of code that performs a series of operations (such as filter, extract, aggregate, operate). More on these operations on Collections (and streams created from collections) in a different article.

imageIn this article I want to show some simple – well, simple to understand and use – examples of lambda expressions.

 

The package java.util.function contains a number of important Interface definitions (and multiple sub-interfaces as well):

  • Predicate – lambda expression that evaluates the outcome of a boolean expression through method test() (a Boolean is returned)
  • Consumer – lambda expression that performs an operation using a passed in object through its accept() operation (no result is returned) (note: formerly known as Block)
  • Function – lambda expression that uses an object passed into to produce a resulting object of potentially a different type
  • BiFunction – lambda expression that uses two objects passed into to produce a resulting object of potentially a different type
  • Supplier – lambda expression that produces an object without getting any actual input through its get() operation
  • Combiner – lambda expression that combines two operands to produce a single result.

Function

An important idea with Lamba expressions is the ability to create a function and pass it just like any other parameter. Here is a simple example of a Function that is created locally and is then handed to another method as an input parameter:

package nl.amis.hrm;

import java.time.LocalDate;
import java.time.Month;
import java.util.function.Function;

public class StraightLamba {

    public Integer deriveSomethingFromPerson(Person person, Function<Person, Integer> functionToPerformOnPerson) {
        Integer value = functionToPerformOnPerson.apply(person);
        // do not know here how this value was calculated from person - it is here now, based on functionToPerformOnPerson's result
        System.out.println("Derived in some magical way: " + value + " using " + functionToPerformOnPerson.toString());
        // execute the lamba expression passed in f using p as its input parameter and return whatever its result is
        return value;
    }

    public static void main(String[] args) {
        Person p = new Person("Louise", "Smith", "Dallas", 16000, LocalDate.of(1976, Month.MARCH, 11), Person.Gender.FEMALE);
        StraightLamba sl = new StraightLamba();

        System.out.println("derived from person " + sl.deriveSomethingFromPerson(p, person -> person.getAge()));

        Function<Person, Person> personMorpher = person -> {
            person.setDateOfBirth(person.getDateOfBirth().plusYears(5));
            return person;
        };
        System.out.println("derived from person " + personMorpher.apply(p).getAge());
    }

}

In this code, the method deriveSomethingFromPerson accepts both a Person object and a Function that acts on a Person object to produce an Integer. The method does not know anything about either the Person or the way in which the Function will derive a value from the Person. It only knows that it can execute the apply method on the Function and it will get an Integer value returned if it passes in a Person.

The call to deriveSomethingFromPerson contains an in-line creation of the Function object – as a Lambda expression using the person -> person.getAge() notation. Note: we could have used any valid Java qualifier instead of person.

The next line creates a Function object, showing how an expression with multiple statements can be used in a Function, using the curly braces {} and a return statement since this is after all a Function. Since this Function personMorpher accepts and returns a Person object, we can invoke the apply method on the Function and call getAge() on the result.

The output of all this is shown below:

image

Predicate

The Predicate is used to evaluate an expression that should return a Boolean. Used in a similar way as the Function, it can be seen as a more specialized sibling of the Function:

 

...
    public void doSomethingWithPersonOnCondition(Person p, Predicate<Person> predicate) {
        if (predicate.test(p)) {
            System.out.println("Person "+p+" satisifies predicate condition");
        }
        else {
            System.out.println("Unfortunatley, Person "+p+" does not satisfy predicate condition");
        };
    }

public static void main(String[] args) {
        Person p = new Person("Louise", "Smith", "Dallas", 16000, LocalDate.of(1976, Month.MARCH, 11), Person.Gender.FEMALE);
        StraightLamba sl = new StraightLamba();

        sl.doSomethingWithPersonOnCondition(p, psn -> psn.getAge()<40);
		....
}		
&#91;/code&#93;


<a href="https://technology.amis.nl/wp-content/uploads/2013/10/image20.png"><img style="background-image: none; padding-top: 0px; padding-left: 0px; display: inline; padding-right: 0px; border: 0px;" title="image" alt="image" src="https://technology.amis.nl/wp-content/uploads/2013/10/image_thumb20.png" width="850" height="53" border="0" /></a>

Note: The Predicate Interface contains convenience methods <em>and, negate, or </em>and<em> xor</em> to combine multiple predicates into a new one.
<h3>Consumer</h3>
The Consumer interface contains an <em>accept </em>operation that accepts an object of the specified type. It will execute its expression, using the input for whatever it has to do. It will not produce any output. Multiple Consumers can be chained together using the <em>and</em> method on the interface.

&nbsp;

...
   public void doSomethingWithPerson(Person p, Consumer<Person> c) {
        c.accept(p);
    }

    public static void main(String[] args) {
        Person p = new Person("Louise", "Smith", "Dallas", 16000, LocalDate.of(1976, Month.MARCH, 11), Person.Gender.FEMALE);
        sl.doSomethingWithPerson(p, louise -> {            
                                                System.out.println("Personal Details: ");
                                                System.out.println(louise);
                                              }
                                );
...

image

Supplier

The Supplier interface contains a get operation that is invoked to have the expression executed and thereby produce and return an instance of the indicated type. This next code fragment shows how the Supplier - a lambda expression that contains the functionality to produce Person objects - is passed to a method that also receives a Consumer as its input; this particular consumer will take a Person as input and do something really useful with it:

 

    public void doSomethingWithToBeSuppliedPerson(Supplier<Person> supplier, Consumer<Person> consumer) {
        consumer.accept(supplier.get());        
    }



    public static void main(String[] args) {

...
        Consumer<Person> peopleProcessor = persona -> {            
                                                        System.out.println("Report on some Personal Details: ");
                                                        System.out.println(persona);
                                                      };
        Supplier<Person> frankenstein = () -> { Person psn = new Person("James", "Sculley", "Monstropolis", 2000
		                                                               , LocalDate.of(1996, Month.NOVEMBER, 11), Person.Gender.MALE);
										        return psn;
											  };

        sl.doSomethingWithToBeSuppliedPerson(frankenstein, peopleProcessor);
...

The output of running this code is shown here:

image

Resources

Robust step by step overview of Lamba expressions (not in the context of collections) http://docs.oracle.com/javase/tutorial/java/javaOO/lambdaexpressions.html#approach6

CoreServlets - Tutorial Series for Java 8 - http://www.coreservlets.com/java-8-tutorial/#lambdas-2

About Post Author

Lucas Jellema

Lucas Jellema, active in IT (and with Oracle) since 1994. Oracle ACE Director and Oracle Developer Champion. Solution architect and developer on diverse areas including SQL, JavaScript, Kubernetes & Docker, Machine Learning, Java, SOA and microservices, events in various shapes and forms and many other things. Author of the Oracle Press book Oracle SOA Suite 12c Handbook. Frequent presenter on user groups and community events and conferences such as JavaOne, Oracle Code, CodeOne, NLJUG JFall and Oracle OpenWorld.
Happy
Happy
0 %
Sad
Sad
0 %
Excited
Excited
0 %
Sleepy
Sleepy
0 %
Angry
Angry
0 %
Surprise
Surprise
0 %

Average Rating

5 Star
0%
4 Star
0%
3 Star
0%
2 Star
0%
1 Star
0%

One thought on “Java 8 – The Road to Lambda expressions

Comments are closed.

Next Post

OOW13: Racing Oracle Open World

For me this year’s Open World has three main themes: Community, Cloud and Mobile. Although actually it included a fourth theme: the America’s Cup. The fabulous comeback of Team America and the spectacular sailing lead to an extra vibe during the conference. And although the media footage provided the best […]
%d bloggers like this: