icon picker
Java Lambda Expressions

This post explores Oracle's Java 8 specification for lambda expression , the syntax and use cases. Lambda expressions can be used as method arguments or to iterate, filter, and extract data from a collection.


Functional interfaces

I'm sure you're aware that Java is not considered a functional programming language. It's considered an object oriented programming language but with , we do have functional interfaces. With the introduction of , Oracle added two new packages, the and the .The lambda expressions, also introduced with , use both of these new packages so we wanted to review some of the functional interfaces that are now available.
Definition of functional interface.
It is defined as an interface that contains only one abstract function or method.
Functional interfaces can represent abstract concepts such as functions, actions or predicates.
We will review some of the most commonly used functional interfaces in lambda notation but there are more interfaces available in the package. To see a list of all the functional interfaces available, check out the Javadoc for .
Some of the more commonly used interfaces include Predicate.
Predicates : are Boolean valued functions of one argument meaning they take in one argument, use a test method to evaluate it and return either true or false. Since this is an interface, we will have to override the test method with logic that determines what should be evaluated.
Consumer :The interface consumes the argument. It accepts a single argument and does not return a result.
Function : which transforms a value from one type to another. It accepts one argument and produces a result.
Supplier: supplies a value. It produces a result of a given type. Unlike Functions, Suppliers do not accept arguments but they do return a result.
UnaryOperator : interface takes a single argument and returns a single value and the interface takes two arguments and returns one.
Here, I’m using the interface.
//using the test method of Predicate
Predicate<String> stringLen = (s)-> s.length() < 10;
System.out.println(stringLen.test("Apples") + " - Apples is less than 10");
As you can see, in the angle brackets after the word Predicate I’m forming the interface to expect a String. It has a name of stringLen and it’s equal to on the right hand side is where I’m actually implementing my method.
I’m used lambda notation so it might look a little different than what you’re used to. On the right hand side in parentheses, I list either no arguments, a single argument or multiple arguments. For Predicate, I need to pass in a single argument then I have the arrow which is a hyphen(-) and a greater than sign(>). Make sure those two are together. After that, I have the logic of my Predicate. So in this case, I’m saying if the string.length or the length of the string is < 10, return true. Otherwise, return false. For each of my examples, I print out the result. So calling stringLen.test() with the value “Apples” and it’ll print out true or false.
Here, I’m using the interface.
//Consumer example uses accept method
Consumer<String> consumerStr = (s) -> System.out.println(s.toLowerCase());
consumerStr.accept("ABCDefghijklmnopQRSTuvWxyZ");
Remember, the interface accepts a value but does not return anything. So on the right hand side, I’m passing it, the argument s but the actual method itself is just going to print out that value in all lowercase letters. It doesn’t return anything.
Now, I have a interface which has two values in the angle brackets.
//Function example
Function<Integer,String> converter = (num)-> Integer.toString(num);
System.out.println("length of 26: " + converter.apply(26).length());
The interface, the first value represents the value coming in for the argument and the second value represents the return value. So in this case, I’m passing in an integer and I’m going to return a string. The logic that I use to do that is on the right hand side of the arrow that says integer.toString() num where num is my argument, I’m going to pass in a value of 26 which gets converted to a string which will just be the characters 2 and 6 which will have a length of 2.
Here is my interface.
//Supplier example
Supplier<String> s = ()-> "Java is fun";
System.out.println(s.get());
Remember, the does not contain any arguments. That’s why I have the open and closed parentheses with nothing inside. It’s important to make sure that you understand you have to include the empty argument list. Otherwise, the compiler will think you made a mistake. So we have an empty argument list, our arrow and on the right hand side, I have a string that says “Java is fun”. It uses the get() method which is the functional method for Supplier to get the value of the string and I have that inside of a print line statement so it’ll print it out.
//Binary Operator example
BinaryOperator<Integer> add = (a, b) -> a + b;
System.out.println("add 10 + 25: " + add.apply(10, 25));
The is going to return an integer. On the right hand side, you can see it expects two values, a and b. It’s going to then add them together and return those values. Now it is so far that none of my arguments have their data types explicitly mentioned. That’s because when we’re using lambda notation, it is inferred. So since the is returning an integer, it knows that the two values coming in as arguments, a and b, must also be integers. I’m using the apply method of the to add 10 and 25.
//Unary Operator example
UnaryOperator<String> str = (msg)-> msg.toUpperCase();
System.out.println(str.apply("This is my message in upper case"));
The is going to return a string. It takes in a string( msg) the message, and it converts it to uppercase. On next line, I’m going to print that out using the method .apply().
Here is the full class :
Here is the output: In the Output window,
image.png
the first was the which returned true that the string Apples is less than 10 characters.
Next, I used the interface to take in a series of letters representing the alphabet in all upper and lowercase letters mixed. It used the two lower to print it back out in all lowercase followed by the Function interface which took in an integer, 26, converted it to a string of two characters, two and six and then found the length of that string which was two.
The actually supplied the string, Java is fun. So you can see in line 31 right above, I’m using the s.get. It gets the value, “Java is fun” and prints it to the command line followed by the which took 10 and 25, added them together and gave us 35 and the last one was the that took in a message that was in mixed case and returned that message in all uppercase.
So these are a few of the more commonly used functional interfaces available with . Remember, there are a lot more that you can use so don’t forget to check out that Javadoc on .

Lambda syntax

If you’ve ever written programs that involve threads, you’re familiar with the concept of a .
The does not take any arguments, and we can use lambda notation to help us write the inner class needed to implement this interface.
// Anonymous Inner Class: Runnable
Runnable r1 = new Runnable() {
@Override
public void run() {
System.out.println("run 1");
}
};
Here, I’m using the old style of creating an anonymous inner class.
// Lambda version of Runnable (no arguments)
Runnable r2 = () -> System.out.println("run 2");
It is the new version of implementing that inner class using Runnable r2 equals. Open and close parentheses to indicate no arguments, and we’re printing out run 2.
// Start both threads
r1.run();
r2.run();
Now, we start r1 and r2 by using r1.run(), r2.run(), and that’ll initiate those methods.
I have some additional examples so you can see some more lambda syntax.
//using an existing functional interface BiFunction
BiFunction<String, String, String> concat = (a, b) -> a + b;
String sentence = concat.apply("Today is ", "a great day");
System.out.println(sentence);
Here is the , which has angle brackets with three string values inside. This indicate the two values that are coming in are going to be strings, and the return value will be a string. On the right-hand side is what we call our lambda notation: parentheses around the two arguments, (a, b) , our arrow notation (->), and then a + b. When you use a + b and you have strings, it puts them together. I use the concat.apply() to put together “Today is a great day”.
//example of the Consumer functional interface
Consumer<String> hello = name -> System.out.println("Hello, " + name);
for (String name : Arrays.asList("Duke", "Mickey", "Minnie")) {
hello.accept(name);
}
Here, I’m showing a . In this case, we’re passing in a name and printing out hello with that name. I have it set up so that string name is going to take each name in the Arrays.asList() with Duke, Mickey, and Minnie, and then it’ll invoke the hello.accept(name), which’ll print
Hello, Duke
Hello, Mickey
Hello, Minnie
And finally
//example of passing one value
GreetingFunction greeting = message ->
System.out.println("Java Programming " + message);
greeting.sayMessage("Rocks with lambda expressions");
}
//custom functional interface
@FunctionalInterface
interface GreetingFunction {
void sayMessage(String message);
}
Now, we have an example of passing one value. We have GreetingFunction greeting = message. message is the one parameter that’s getting passed. Notice that, we’re actually creating the functional interface called GreetingFunction. I wanted to show you how you can create your own interface, as well as use the existing ones in the java.util.function.
Here the class:
Now if you run this program, you'll see the first two threads print out as:
image.png
I hope these examples are starting to make sense as far as the syntax of a lambda notation.

Methods as lambdas

Another feature of is we can now take any type of method and convert it into a lambda including static method, instance methods, and even constructors.
Do you notice any syntax that looks a little different? If you've programmed in C++ before you might recognize this symbol that I'm referring to. Notice on the right hand side where the lambda notation would normally be, we have integer::ToString. This is the structure that creates a lambda from a method. It's called a method reference and it enables us to pass references of methods or constructors via the :: syntax.
IntFunction<String> intToString = num -> Integer.toString(num);
System.out.println("expected value 3, actual value: " +
intToString.apply(123).length());
we have the interface int function which is going to return a string and take in an integer. It's a special function in function, we don't have to specify the data type of our argument num because the interface provides enough information to the compiler for it to infer that num is going to be an integer. On the right hand side of our arrow we have integer.toString which takes num, converts it to a string.
Next we're printing out the value, intTwoString.apply where apply is the functional method of the int function interface. It'll convert the number, 123, to a string and get the length.
//static method reference using ::
IntFunction<String> intToString2 = Integer::toString;
System.out.println("expected value 4, actual value: " +
intToString2.apply(4567).length());
And as I stated we're using this new method reference, the ::. So this time we don't even specify the variable name or the argument on the right hand side. We just have integer::toString. Now the compiler is smart enough to know that it should expect an integer and return a string. I'm actually going to use the 4567 which is an integer which will be converted to a string.
There's a few more examples.
//lambdas made using a constructor
Function<String,BigInteger> newBigInt = BigInteger::new;
System.out.println("expected value: 123456789, actual value: "+
newBigInt.apply("123456789"));
I'm using the function interface which has two values in the angle brackets. It's saying I'm going to give you a string and I want you to return a big integer.
On the right hand side I have an example of how we can use the colon colon with a constructor. So it's saying that whatever string is coming in I'm going to create a new big integer. So I'm going to convert the string value to a big integer. We use the .apply for the function interface and I'm giving it a string that has the values 123456789 and it will convert that to an integer.
//example of a lambda made from an instance method
Consumer<String> print = System.out::println;
print.accept("Coming to you directly from a lambda...");
This is the consumer interface. Remember the consumer interface consumes data but does not return anything. So I have print.accept(). That is the method name of the functional method for the consumer interface. And I'm giving it the string coming to you directly from a lambda. Now on next line in the right hand side notice the print line is an instance method of system.out. It will automatically take in the string, provide it on next line and print it to the console.
//these two are the same using the static method concat
UnaryOperator<String> greeting = x -> "Hello, ".concat(x);
System.out.println(greeting.apply("World"));
UnaryOperator<String> makeGreeting = "Hello, "::concat;
System.out.println(makeGreeting.apply("Peggy"));
The last example is the unary operator which is going to operate on a string. It has the value hello::concat. The concat is the method that I'm actually going to be using, and again I'm not specifying the fact that I'm going to provide a second string. It can infer that.
Let's run the program and take a look at the output.
image.png
As you can see in it's starting to make things a little simpler as far as the amount of code that we need to write. In this particular example we were making methods into lambdas whether those methods were static methods, instant methods or even constructors.

Create new functional interfaces

Although Java 8 has included a new package, java.util.function, that includes many different functional interfaces, sometimes we just need to create our own functional interface.
We can create a new functional interface by creating a new file or we can include the functional interface inside of our class file. Either way when creating our own functional interfaces it's helpful to add annotation that says @FunctionalInterface to help ensure this is a functional interface.
As you can see
@FunctionalInterface
interface Calculate {
int calc(int x, int y);
}
I have the at functional interface. Now this particular interface has only one method header. A functional interface is an interface that has only one method.
The only code that I need in here is the name of the interface and then the method header or the method signature. In this case i, int calc is the name of my method and it takes two parameters that are both integers. I do need to specify the data types for each value here. The return type and the two arguments.
Calculate add =(a,b)-> a + b;
Calculate difference = (a,b) -> Math.abs(a-b);
Calculate divide =(a,b)-> (b != 0 ? a/b : 0);
This is really nice because my interface is very simple. It simply has the interface name and the one method signature. And then using the lambdas I can define that method signature in different ways. Add, difference, divide and multiply.

Collections

Since collections are used so heavily in , I wanted to do a quick review. The API was introduced with .
A collection is an object that groups multiple elements into a single unit.
Collections are used to store, retrieve, manipulate, and communicate aggregate data.
The collections API provides the following interfaces:
set : which is a collection that does not contain duplicates.
list : which is just an ordered collection based on the way the user entered the data.
map : IT is an object that maps keys to values.
The collection interface contains methods that perform basic operations such as
int size() : which gets the size of the collection.
boolean isEmpty() returns true if it's empty, false if it's not.
boolean contains() : checks to see if an element is inside the collection, returning true if it is and false if not
add() : an element returns true if it was able to add the element, or false if there was an error.
boolean remove() : which tries to remove an element. If the element is not found, it returns false. If it successfully removes it, it'll return true.
iterator() : it returns an iterator over the elements in this collection.
Now let's review some examples of collections.
I have a program that lists some collections. I'm going to start by using collections and not using the lambda notation, then I'm going to show how the lambda notations can simplify our code.
Loading…
I create a list of names.
List<String> names = Arrays.asList("Paul", "Jane", "Michaela", "Sam");
//way to sort prior to Java 8 lambdas
Collections.sort(names, new Comparator<String>() {
@Override
public int compare(String a, String b) {
return b.compareTo(a);
}
});
I have four names in my list. In order to sort those, prior to using the Java 8 lambdas. we could say Collections.sort() and provide the variable names and new Comperator, and then we'd have to override the collections interface, the method compare, to tell it how to do the compare.
//first iteration with lambda
Collections.sort(names, (String a, String b) -> {
return b.compareTo(a);
});
Now I have the next iteration which uses lambda, but it's still not quite the most concise.
//now remove the return statement
Collections.sort(names, (String a, String b) -> b.compareTo(a));
This is the best version of this code using lambda notation. Remember we don't have to specify the data types in the lambda notation because it can be inferred since there is only one method inside the Collections interface.
//now remove the data types and allow the compile to infer the type
Collections.sort(names, (a, b) -> b.compareTo(a));
In this case the sort method takes in a list called names, it takes in two values, a and b, and compares b to a, returning them in the right order.
All right, next I wanted to show you how we could do a book collection. So I've created three random books here.
//total pages in your book collection
Book book1 = new Book("Miss Peregrine's Home for Peculiar Children",
"Ranson", "Riggs", 382);
Book book2 = new Book("Harry Potter and The Sorcerers Stone",
"JK", "Rowling", 411);
Book book3 = new Book("The Cat in the Hat",
"Dr", "Seuss", 45);
I do have a Book class, so let's take a quick look at that.
public Book(String title, String authorFName, String authorLName,
int pages) {
this.title = title;
this.authorFName = authorFName;
this.authorLName = authorLName;
this.pages = pages;
}
In my Book class I have a constructor that takes all the values and then I have all my get and set methods.
I'm adding the three books that I created above.
List<Book> books = Arrays.asList(book1, book2, book3);
int total = books.stream()
.collect(Collectors.summingInt(Book::getPages));
System.out.println(total);
On above I wanted to show you how we could use the .collect. So the .collect uses the Collections, so it uses Collectors.summingInt and it uses the getPages method of the Book class to get the number of pages. It adds them all together and puts them into the variable total and then prints them out.
//aggregate author first names into a list
List<String> list = books.stream()
.map(Book::getAuthorLName)
.collect(Collectors.toList());
System.out.println(list);
Now I am creating another list = books.stream, I'm using the .map to get the last name of the author. Notice I'm using our method reference that we talked about earlier, the ::, Here I'm using the .collect again, this time instead of adding together the values of an integer variable as getPages, this time I'm putting a list together of the authors' last names and then I'm printing out the list.
The next example creates a list that has duplicate books.
//create a list with duplicates
List<Book> dupBooks = Arrays.asList(book1, book2, book3, book1, book2);
System.out.println("Before removing duplicates: ");
System.out.println(dupBooks.toString());
So on the right hand side I just added book1 and book2 in a second time. I'm printing them out so we can see what it looks like before we eliminate the duplicates.
Collection<Book> noDups = new HashSet<>(dupBooks);
System.out.println("After removing duplicates: ");
System.out.println(noDups.toString());
Then next I'm creating a new collection and this time I'm using a HashSet. Remember, the set interface will automatically eliminate duplicates, so next I'm printing out this new list of books.
Finally, I wanted to show you how we could use the set interface, and this time I'm also using HashSet,
//example of using Set to eliminate dups and sort automatically
Set<Integer> numbers = new HashSet<>(asList(4, 3, 3, 3, 2, 1, 1, 1));
System.out.println(numbers.toString());
I'm providing a list that has several numbers, and many of them are repeated. What will happen is it will actually take the values in the list and it will eliminate all the duplicates and then print it out.
Let's run the program to take a look.
image.png
At the very top, remember we added up the total pages of all the books?
Next we use the .collect to collect the last names of the authors and printed those out, so we have Riggs, Rowling, and Seuss.
Next I print out the names of the books and the authors for each of the books in our list.
Then when I created the set using the exact same list, notice this time it removes the duplicates, because the set interface does not allow duplicates.
And finally, the very last thing I did was use a list that had the numbers 4, 3, 3, 3, 2, 1, 1, 1, and when I used the set interface, it automatically removes duplicates and puts them in sorted order.
In JDK 8 and later, the preferred method of iterating over a collection is to obtain a stream and perform aggregate operations on that.
Aggregate operations are often used in conjunction with lambda operations to make programming more expressive and using less lines of code. For more information on collections, check out the

Streams

The package contains the majority of the interfaces and classes for the stream functionality. A stream in this case represents a sequence of elements. The stream package was added in specifically to help traverse collections. Most stream operations accept some kind of lambda expression parameter which is a functional interface specifying the exact behavior of the operation.
Stream operations are categorized as either intermediate or terminal.
Terminal operations are either void or return a result of a certain type.
Intermediate operations return the stream itself. Intermediate operations are useful to allow us to chain multiple method calls in a row on a single stream.
Some of the commonly used operations include
, filter and where map and filter are intermediate operations. They allow us to map one value to another or to filter the results using a Predicate. is a terminal operator.
Two more commonly used operations are sorted and collect.
Sorted is an intermediate operation which returns a sorted view of the stream but remember, the original collection is not being changed.
Want to print your doc?
This is not the way.
Try clicking the ⋯ next to your doc name or using a keyboard shortcut (
CtrlP
) instead.