Functional Programming in Java
Java 8
1.Lambda expression
2.Functional Interface
3.Stream Operation

Hello, Lambda Expressions!
Change the Way You Think
Imperative style—that’s what Java has provided us since its inception. In this style, we tell Java every step of what we want it to do and then we watch it faithfully exercise those steps. That’s worked fine, but it’s a bit low level.
The code tends to get verbose, and we often wish the language were a tad more intelligent;
we could then tell it—declaratively—what we want rather than
delve into how to do it.
The Habitual Way:
Imperative way
boolean found = false;
for(String city : cities) {
if(city.equals("Chicago")) {
found = true;
break;
}
}
System.out.println("Found chicago?:" + found);A Better way:
System.out.println("Found chicago?:" + cities.contains("Chicago"));Tangible Improvements
- No messing around with mutable variables
- Iteration steps wrapped under the hood
- Less clutter
- Better clarity; retains our focus
- Less impedance; code closely trails the business intent
- Less error prone
- Easier to understand and maintain
Beyond Simple Case
final List<BigDecimal> prices = Arrays.asList(
new BigDecimal("10"), new BigDecimal("30"), new BigDecimal("17"),
new BigDecimal("20"), new BigDecimal("15"), new BigDecimal("18"),
new BigDecimal("45"), new BigDecimal("12"));
BigDecimal totalOfDiscountedPrices = BigDecimal.ZERO;
for(BigDecimal price : prices) {
if(price.compareTo(BigDecimal.valueOf(20)) > 0)
totalOfDiscountedPrices =
totalOfDiscountedPrices.add(price.multiply(BigDecimal.valueOf(0.9)));
}
System.out.println("Total of discounted prices: " + totalOfDiscountedPrices); Suppose we’re asked to total the prices greater than $20, discounted by 10%.
Let’s do that in the habitual Java way first.
A Better Way, Again
final BigDecimal totalOfDiscountedPrices =
prices.stream()
.filter(price -> price.compareTo(BigDecimal.valueOf(20)) > 0)
.map(price -> price.multiply(BigDecimal.valueOf(0.9)))
.reduce(BigDecimal.ZERO, BigDecimal::add);
System.out.println("Total of discounted prices: " + totalOfDiscountedPrices); The looping is concealed much like it was under the contains() method. The
map() method (and the filter() method), however, is more sophisticated. For each
price in the prices list, it invokes the provided lambda expression and puts the
responses from these calls into a new collection. The reduce() method is invoked
on this collection to get the final result.
Why Code in the Functional Style?
- Enforcing Policies
Transaction transaction = getFromTransactionFactory();
//... operation to run within the transaction ...
checkProgressAndCommitOrRollbackTransaction();
UpdateAuditTrail();runWithinTransaction((Transaction transaction) -> {
//... operation to run within the transaction ...
});- Telling the Story
if the code reads more like the way the business states the problem, it becomes easier to read, easier to discuss with the business folks, and easier to evolve to meet their changing demands.
tickers.map(StockUtil::getprice).filter(StockUtil::priceIsLessThan500).sum()Recap
It’s a whole new world in Java. We can now program in an elegant and fluent functional style, with higher-order functions. This can lead to concise code that has fewer errors and is easier to understand, maintain, and parallelize.
The Java compiler works its magic so we can send lambda expressions or method references where functional interfaces are expected.
Using Collections
Iterating through a List
final List<String> friends =
Arrays.asList("Brian", "Nate", "Neal", "Raju", "Sara", "Scott");
for(int i = 0; i < friends.size(); i++) {
System.out.println(friends.get(i));
} The Iterable interface has been enhanced in Java Development Kit (JDK) 8 with a special method named forEach(), which accepts a parameter of type Consumer.
friends.forEach(new Consumer<String>() {
public void accept(final String name) {
System.out.println(name);
}
});friends.forEach((final String name) ->
System.out.println(name));friends.forEach((name) ->
System.out.println(name));friends.forEach(System.out::println);Transforming a List
final List<String> uppercaseNames = new ArrayList<String>();
for(String name : friends) {
uppercaseNames.add(name.toUpperCase());
} In this imperative style, we created an empty list then populated it with alluppercase names, one element at a time
We used the internal iterator, but that still required the empty list and the effort to add elements to it. We can do a lot better.
final List<String> uppercaseNames = new ArrayList<String>();
friends.forEach(name -> uppercaseNames.add(name.toUpperCase()));
System.out.println(uppercaseNames);friends.stream()
.map(name -> name.toUpperCase())
.forEach(name -> System.out.print(name + " "));The Stream’s map() method can map or transform a sequence of input to a sequence of output—that fits quite well for the task at hand.
Using Method Reference
We can nudge the code to be just a bit more concise by using a feature called
method reference. The Java compiler will take either a lambda expression or
a reference to a method where an implementation of a functional interface is
expected. With this feature, a short String::toUpperCase can replace name ->
name.toUpperCase()
friends.stream()
.map(String::toUpperCase)
.forEach(name -> System.out.println(name));Finding Element
From a list of names, let’s pick the ones that start with the letter N. Since
there may be zero matching names in the list, the result may be an empty
list. Let’s first code it using the old approach.
final List<String> startsWithN = new ArrayList<String>();
for(String name : friends) {
if(name.startsWith("N")) {
startsWithN.add(name);
}
} Let’s refactor this code to use the filter() method, and see how it changes things
final List<String> startsWithN =
friends.stream()
.filter(name -> name.startsWith("N"))
.collect(Collectors.toList());Reusing Lambda Expression
final List<String> friends =
Arrays.asList("Brian", "Nate", "Neal", "Raju", "Sara", "Scott");
final List<String> comrades =
Arrays.asList("Kate", "Ken", "Nick", "Paula", "Zach");
final List<String> editors =
Arrays.asList("Brian", "Jackie", "John", "Mike");final long countFriendsStartN =
friends.stream().filter(name -> name.startsWith("N")).count();
final long countComradesStartN =
comrades.stream().filter(name -> name.startsWith("N")).count();
final long countEditorsStartN =
editors.stream().filter(name -> name.startsWith("N")).count(); We want to filter out names that start with a certain letter. Let’s first take a naive approach to this using the filter() method.
Let’s refactor the previous code to make it DRY.1 (See the Don’t Repeat Yourself—DRY—principle)
final Predicate<String> startsWithN = name -> name.startsWith("N");
final long countFriendsStartN =
friends.stream().filter(startsWithN).count();Duplication in Lambda Expressions
final Predicate<String> startsWithN = name -> name.startsWith("N");
final Predicate<String> startsWithB = name -> name.startsWith("B");
final long countFriendsStartN =
friends.stream().filter(startsWithN).count();
final long countFriendsStartB =
friends.stream().filter(startsWithB).count(); Let’s pick the names that start with N or B from the friends collection of names.Continuing with the previous example, we may be tempted to write something
like the following:
Removing Duplication with Lexical coping
public static Predicate<String> checkIfStartsWith(final String letter) {
return name -> name.startsWith(letter);
}
final long countFriendsStartN =
friends.stream().filter(checkIfStartsWith("N")).count();
final long countFriendsStartB =
friends.stream().filter(checkIfStartsWith("B")).count(); Java reaches over to the scope of the definition of this lambda expression and finds the variable letter in that scope. This is called lexical
scoping. it’s also referred to as a closure
Refactoring to Narrow the Scop
final Function<String, Predicate<String>> startsWithLetter =
(String letter) -> {
Predicate<String> checkStartsWith =
(String name) -> name.startsWith(letter);
return checkStartsWith;
}; In the preceding (smelly) example we used a static method, but we don’t want to pollute the class with static methods to cache each variable in the future.
It would be nice to narrow the function’s scope to where it’s needed. We can
do that using a Function class.
Our examples illustrate how to pass functions to functions, create functions within functions,and return functions from within functions.
final Function<String, Predicate<String>> startsWithLetter =
letter -> name -> name.startsWith(letter);
final long countFriendsStartN =
friends.stream().filter(startsWithLetter.apply("N")).count();
final long countFriendsStartB =
friends.stream().filter(startsWithLetter.apply("B")).count(); Picking an Element
public static void pickName(
final List<String> names,
final String startingLetter) {
String foundName = null;
for(String name : names) {
if(name.startsWith(startingLetter)) {
foundName = name;
break;
}
}
System.out.print(
String.format("A name starting with %s: ",
startingLetter));
if(foundName != null) {
System.out.println(foundName);
} else {
System.out.println("No name found");
}
}public static void pickName(
final List<String> names, final String startingLetter) {
final Optional<String> foundName =
names.stream()
.filter(name ->name.startsWith(startingLetter))
.findFirst();
System.out.println(
String.format("A name starting with %s: %s",
startingLetter, foundName.orElse("No name found")
)
);
}FP List
public static final class NonEmptyList<T> implements List<T> {
public T head() { return _head; }
public List<T> tail() { return _tail; }
public boolean isEmpty() { return false; }
protected NonEmptyList(T head, List<T> tail) {
this._head = head;
this._tail = tail;
}
private final T _head;
private final List<T> _tail;
@Override
public boolean equals(Object other) {
if (other == null || getClass() != other.getClass()) return false;
List<?> that = (List<?>) other;
return head().equals(that.head()) && tail().equals(that.tail());
}
@Override
public int hashCode() {
return 37*(head().hashCode()+tail().hashCode());
}
@Override
public String toString() {
return "(" + head() + ", " + tail() + ")";
}
}FP List(filter,map,fold)
public static final class NonEmptyList<T> implements List<T> {
public List<T> filter (Function1<T,Boolean> f) {
if (f.apply(head())) {
return list(head(), tail().filter(f));
} else {
return tail().filter(f);
}
}
public <T2> List<T2> map (Function1<T,T2> f) {
return list(f.apply(head()), tail().map(f));
}
public <T2> T2 foldLeft (T2 seed, Function2<T2,T,T2> f) {
return tail().foldLeft(f.apply(seed, head()), f);
}
public <T2> T2 foldRight (T2 seed, Function2<T,T2,T2> f) {
return f.apply(head(), tail().foldRight(seed, f));
}
public void foreach (Function1Void<T> f) {
f.apply(head());
tail().foreach(f);
}
} Starter Set of Functional Interfaces
| Description | Represents an operation that will accept an input and returns nothing. |
|---|---|
| Abstract method | accept() |
| Default method(s) | andThen() |
| Popular usage | As a parameter to the forEach() method |
| Primitive specializations | IntConsumer, LongConsumer, DoubleConsumer, … |
Consumer<T>
| Description | A factory that’s expected to return either a new instance or a precreated instance |
|---|---|
| Abstract method | get() |
| Default method(s) | -- |
| Popular usage | To create lazy infinite Streams |
| Primitive specializations | IntSupplier, LongSupplier, DoubleSupplier, … |
Supplier<T>
| Description | Useful for checking if an input argument satisfies some condition |
|---|---|
| Abstract method | test() |
| Default method(s) | and(), negate(), and or() |
| Popular usage | As a parameter to Stream’s methods, like filter() and anyMatch() |
| Primitive specializations | IntPredicate, LongPredicate, DoublePredicate, … |
Predicate<T>
| Description | A transformational interface that represents an operation intended to take in an argument and return an appropriate result |
|---|---|
| Abstract method | apply() |
| Default method(s) | andThen(), compose() |
| Popular usage | As a parameter to Stream’s map() method |
| Primitive specializations | IntFunction, LongFunction, DoubleFunction, IntToDoubleFunction, DoubleToIntFunction, … |