Search icon CANCEL
Subscription
0
Cart icon
Your Cart (0 item)
Close icon
You have no products in your basket yet
Arrow left icon
Explore Products
Best Sellers
New Releases
Books
Videos
Audiobooks
Learning Hub
Free Learning
Arrow right icon
Learning Java Functional Programming
Learning Java Functional Programming

Learning Java Functional Programming: Create robust and maintainable Java applications using the functional style of programming

Arrow left icon
Profile Icon Richard M Reese Profile Icon Richard M. Reese
Arrow right icon
$60.99
Full star icon Full star icon Full star icon Empty star icon Empty star icon 3 (1 Ratings)
Paperback Oct 2015 296 pages 1st Edition
eBook
$9.99 $47.99
Paperback
$60.99
Subscription
Free Trial
Renews at $19.99p/m
Arrow left icon
Profile Icon Richard M Reese Profile Icon Richard M. Reese
Arrow right icon
$60.99
Full star icon Full star icon Full star icon Empty star icon Empty star icon 3 (1 Ratings)
Paperback Oct 2015 296 pages 1st Edition
eBook
$9.99 $47.99
Paperback
$60.99
Subscription
Free Trial
Renews at $19.99p/m
eBook
$9.99 $47.99
Paperback
$60.99
Subscription
Free Trial
Renews at $19.99p/m

What do you get with Print?

Product feature icon Instant access to your digital eBook copy whilst your Print order is Shipped
Product feature icon Paperback book shipped to your preferred address
Product feature icon Download this book in EPUB and PDF formats
Product feature icon Access this title in our online reader with advanced features
Product feature icon DRM FREE - Read whenever, wherever and however you want
OR
Modal Close icon
Payment Processing...
tick Completed

Shipping Address

Billing Address

Shipping Methods
Table of content icon View table of contents Preview book icon Preview Book

Learning Java Functional Programming

Chapter 1. Getting Started with Functional Programming

Functional programming languages have been used successfully for decades and present a different and often more elegant way of expressing the program logic. Functional languages, such as Lisp, Clojure, or Haskell, incorporate techniques that may seem odd and hard to follow by programmers who are used to imperative programming techniques.

A language such as Java, while not initially developed with a functional orientation, can incorporate functional techniques. This is the major change to the language made with the release of Java 8. Java now incorporates imperative, procedural, object-oriented, and functional techniques.

It is possible to write a non-object-oriented program in Java. Likewise, it is possible to write a nonfunctional program in Java 8. The goal of this book is to enlighten the reader to the nature of functional programming techniques and how to incorporate these techniques in Java 8 applications.

We will start with a discussion of the attributes commonly associated with functional programming. From there, we will examine the support, Java provides for developing applications using a functional-style programming approach.

A predominant feature of functional programming languages is the use of functions. The term, function, is generally understood to be:

  • A way of expressing an algorithm
  • A mathematical function
  • Where the goal is to avoid state changes and mutable data

In functional programming, applications are constructed using only pure functions. A pure function is a function which does not have side effects. A side effect occurs when a function does something else besides simply returning a value, such as mutating a global variable or performing IO. In this chapter, we will examine the major aspects of functional programming including:

  • Functions and function composition
  • Fluent interfaces
  • Strict and non-strict evaluation
  • Persistent data structures, monads, and the Optional class
  • Recursion and parallelism

This is followed by the support Java 8 provides for functional programming, including:

  • Lambda expressions
  • Default methods
  • Functional interface
  • Method and constructor references
  • Collections

In addition, to our discussion of functional programming support as provided by Java 8, refactoring, debugging, and testing Java 8 code are also important topics, which need to be addressed. These topics are covered in Chapter 8, Refactoring, Debugging, and Testing.

So, let's begin with an overview of what constitutes the functional programming approach.

Aspects of functional programming

Functions can be simple or complex, but simpler functions are preferred. The function should ideally not change the state of memory or perform I/O, and consequently work with immutable data. These later two concepts are explored in Chapter 6, Optional and Monads.

There are several aspects of functional programming languages that we will explore here. They include:

  • Functions
  • Function composition
  • Fluent interfaces
  • Strict versus non-strict evaluation
  • Parallelism
  • Persistent data structures
  • Recursion
  • Optional and monads

Each of these concepts will be introduced in the following sections. We will explore the nature of each concept, explain why it is important, and when practical provide simple examples using Java.

Functions

Functions are the foundation of functional programming languages. They play a central role in supporting other functional programming concepts. In this section, we will introduce many of the terms used to describe functions including high-order, first-class, and pure functions. The concepts of closure and currying will also be explained.

First-class and high-order functions are associated with functional programming. A first-class function is a computer science term. It refers to functions that can be used anywhere a first-class entity can be used. A first-class entity includes elements such as numbers and strings. They can be used as an argument to a function, returned from a function, or assigned to a variable.

High-order functions depend upon the existence of first-class functions. They are functions that either:

  • Take a function as an argument
  • Return a function

Java 8 has introduced the concept of lambda expressions to the language. These are essentially anonymous functions that can be passed to and returned from functions. They can also be assigned to a variable. The basic form of a lambda expression follows where a parameter, such as x, is passed to the body of the function. The lambda operator, ->, separates the parameter from the body. This function is passed a value, which is multiplied by two and then returned, as follows:

x -> 2 * x

In this lambda expression, it is assumed that an integer is passed and that integer is returned. However, the data type is not restricted to an integer as we will see later. In the following lambda expression, an argument is passed and nothing is returned:

x->System.out.println(x)

Lambda expressions must be used in the proper context. It would not be appropriate to pass a lambda expression, which returns a value to a method, to a function that cannot use the returned value.

We can use the previous expression in many places that expect a single value being passed and nothing to be returned as shown next. In the following example, an array of integers is converted to a list. The lambda expression is then used as an argument to the List class's forEach method, which displays each element of the list. The forEach method applies the lambda expression to each element in the list, avoiding having to create an explicit loop to achieve the same effect:

        Integer arr[] = {1,2,3,4,5};
        List<Integer> list = Arrays.asList(arr);
        list.forEach(x->System.out.println(x));

Tip

Downloading the example code

You can download the example code files from your account at http://www.packtpub.com for all the Packt Publishing books you have purchased. If you purchased this book elsewhere, you can visit http://www.packtpub.com/support and register to have the files e-mailed directly to you.

The output will list the numbers one to five on separate lines.

Changing a program's state is avoided in functional programming. Calling a function with the same input values should result in the same behavior each time. This makes it easier to understand the function. Imperative programming changes the state using statements such as the assignment statement.

A pure function is a function that has no side effects. This means that memory external to the function is not modified, IO is not performed, and no exceptions are thrown. With a pure function, when it is called repeatedly with the same parameters, it will return the same value. This is called referential transparency.

With referential transparency, it is permissible to modify local variables within the function as this does not change the state of the program. Any changes are not seen outside of the function.

Advantages of pure function include:

  • The function can be called repeatedly with the same argument and get the same results. This enables caching optimization (memorization).
  • With no dependencies between multiple pure functions, they can be reordered and performed in parallel. They are essentially thread safe.
  • Pure function enables lazy evaluation as discussed later in the Strict versus non-strict evaluation section. This implies that the execution of the function can be delayed and its results can be cached potentially improving the performance of a program.
  • If the result of a function is not used, then it can be removed since it does not affect other operations.

There are several other terms associated with functions, such as the term closure. This refers to a function passed around along with its environment. The environment consists of the variables it uses. Java 8 supports a form of closure, and will be illustrated in Chapter 2, Putting the Function in Functional Programming.

Currying is the process of evaluating multiple arguments of a function one-by-one, producing intermediate results. In the process, we introduce a new function with one less argument than the previous step. For example, let's start with this function:

Functions

We can evaluate it for the value of 3 and 4 as follows, returning a result of 7:

Functions

If we substitute 3 for x we get:

Functions

Next, if we define g(y) as:

Functions

Then, the following is also true:

Functions

We reduced the number of arguments from two to one. Using a value of 4 for y yields the original result of 7. The process of currying, and partially applying functions, permit high-order functions to be used more effectively. This will become clearer in Chapter 2, Putting the Function in Functional Programming.

Function composition

Imperative programming places emphasis on a step-by-step process to implement an application. This is typified by a logical set of steps where code is executed using basic control constructs and is often encapsulated in functions or procedures.

Functional programming places more emphasis on how these functions are arranged and combined. It is this composition of functions, which typifies a functional style of programming. Functions are not only used to organize the execution process, but are also passed and returned from functions. Often data and the functions acting on the data are passed together promoting more capable and expressive programs.

We will illustrate this technique using the Function interface as defined in the java.util.function package. This interface possesses a compose and andThen methods. Both of these methods return a composed function.

The compose method will execute the function passed to it first, and then uses its result with the function the compose method is executed against. The andThen method will execute the first function and then execute the function passed as an argument to the andThen method.

The next code sequence demonstrates the compose method, which is passed as a function to take the absolute value of a number. The absThenNegate variable is assigned a function that will also negate the number. This variable is declared as a Function type, which means that the function assigned to it expects to be passed as an integer and returns an integer.

This function will execute the argument of the compose method and the Math class's abs method first, against some value, and then apply the negateExact method to this result. In other words, it will take the absolute value of a number and then negate it. Both of these methods are expressed as method references, which are new to Java 8. A method reference consist of the class name followed by a set of double colons, and then a method name providing a simpler form of method invocation:

Function<Integer,Integer>absThenNegate = 
    ((Function<Integer,Integer>)Math::negateExact)
        .compose(Math::abs);

This is illustrated with the following sequence. The Function interface's apply method is used to invoke the composed function:

System.out.println(absThenNegate.apply(-25));
System.out.println(absThenNegate.apply(25));

Both of these statements will display a -25. In the first statement, the absolute value of a -25 is obtained and then negated. The second statement works the same way except its argument is +25.

The negateThenAbs variable that follows, illustrates the andThen method. The function used as an argument to the andThen method is applied after the first function is executed. In this case, the negateExact method is executed first and then the abs function is applied:

Function<Integer,Integer>negateThenAbs = 
    ((Function<Integer,Integer>)Math::negateExact)
        .andThen(Math::abs);
System.out.println(negateThenAbs.apply(-25));
System.out.println(negateThenAbs.apply(25));

The output of both display statements will be 25.

We could have obtained the same results with a series of imperative statements. However, this does not result in as much flexibility as can be obtained using function composition. The ability to pass functions will provide the enhanced flexibility. We will postpone a detailed discussion of this approach until Chapter 3, Function Composition and Fluent Interfaces.

Fluent interfaces

Fluent interfaces constitute a way of composing expressions that are easier to write and understand. A fluent interface is often implemented using method chaining, sometimes called method cascading, where the returned value is used again in the same context.

In Java 8, the use of fluent interfaces is found in numerous places. We will illustrate this style with an example using the new Date and Time API.

Suppose we want to calculate a new date that is 2 years in the future, minus 1 month plus 3 days. We can use the following code sequence to achieve this result. The LocalDate class's method now returns an instance of the LocalDate class representing the current date. This date is the base for creating a new day called futureDate:

LocalDate today = LocalDate.now();
LocalDate futureDate = today.plusYears(2);
futureDate = futureDate.minusMonths(1);
futureDate = futureDate.plusDays(3);
System.out.println(today);
System.out.println(futureDate);

This will generate the following output:

2015-03-22
2017-02-25

Contrast this with the next code sequence, which takes advantage of the APIs fluent interface and produces the same output:

LocalDatefutureDate = LocalDate.now()
    .plusYears(2)
    .minusMonths(1)
    .plusDays(3);

The code flow is easy to read and flows in a more natural way. You will see repeated usage of fluent interfaces in the book. Streams use this approach consistently.

Strict versus non-strict evaluation

Functional languages can be classified as either using strict or non-strict evaluation of expressions. With strict evaluation, sometimes called eager evaluation, the expressions are evaluated as they are encountered.

With non-strict evaluation, they are not evaluated until necessary. Non-strict evaluation is sometimes called lazy evaluation. However, these terms are not always strict synonyms. Non-strict evaluation is concerned with the semantics of the expression, while lazy evaluation deals more with how the expression is evaluated.

Lazy evaluation is supported using streams. A stream can be thought of as a series of elements that flow like a river or stream. They add a convenient means of processing data in an easy-to-use and natural manner. The stream concept is support in Java 8 with the Stream class.

In the following sequence, a stream is created by generating five random numbers, sorting these numbers, and then displaying them:

Random random = new Random();
random.ints()
    .limit(5) 
    .sorted()
    .forEach(x->System.out.println(x));

The ints method returns an IntStream instance. The limit method will restrict the stream to the first five numbers, and the sorted method will sort these numbers. However, the stream is not evaluated until a terminal method such as the forEach method is encountered. The use of streams will be demonstrated in more detail in Chapter 4, Streams and the Evaluation of Expressions.

One possible output follows:

-1790144043
-1777206416
23979263
801416357
874941096

A stream is processed lazily, which enables the runtime system to optimize how the stream's component operations are executed. In addition, they are used in a fluent manner.

Persistent data structures

Persistent data structures maintain previous versions of itself. As changes to the data structure occur, a new version of the data structure is created while maintaining the older version. These structures are effectively immutable.

Avoiding the mutation of data obviously has no side effects. This means that they are thread-safe and enable various optimization techniques.

One consequence of mutable data is that, when accessed from multiple threads, many threading issues arise that can make a program less reliable and maintainable. If the data is immutable, then these threading issues such as the need to synchronize data access largely go away.

One approach used by functional programming languages to simulate state is using a data structure, which is passed to a function. The data structure is copied and any changes made are reflected in the new copy of the data structure. This is referred to a state-passing style and can use a considerable amount of memory unless appropriate optimization techniques are applied.

There are immutable collections that support the concept of persistent data structures. However, when a language is entirely immutable, then a large amount of garbage is generated requiring extensive optimization to be useful. Some of these collections are not practical when they contain a significant number of elements.

We are not able to show how Java can support this concept here. However, in Chapter 6, Optional and Monads we will examine techniques that can be used in Java 8 to support data structures, such as some monads, in more detail.

Recursion

A loop is used in an imperative language to perform repeated calculations. Recursion is a technique that can achieve the same effect but often in a more elegant manner. A function is recursive if it calls itself either directly or indirectly. For example, calculating the factorial of a number can be accomplished using either iteration or recursion. The factorial of a number is defined as follows:

Recursion
Recursion

where n>0

An iterative solution follows:

int result = 1;
for (int i = 5; i>= 1; i--) {
    result = result * i;
}
System.out.println(result);

The output will be 120. The equivalent recursion solution starts with a recursive factorial function:

public int factorial(int n) {
    if(n==1) {
        return 1;
    } else {
        return n * factorial(n-1);
    }
}

This solution is more succinct than the iterative version and more closely matches the problem definition. The function can be invoked as follows:

System.out.println(factorial(num));

It will generate the same output: 120.

Indirect recursion occurs when a function calls itself but not immediately. For example, if function A calls function B, which then calls function A, we have indirect recursion.

A recursion function needs to be bounded. This means that it must stop calling itself at some time. Otherwise, it will exceed the system's resources and result in an exception being thrown and the program terminating abnormally. In the factorial function, the test for an n value of 1 stopped the recursion.

Frequently, recursion is implemented using a program stack. However, if tail recursion is used, then the compiler can avoid the use of a program stack and use essentially the same technique used to implement an imperative loop. Tail recursion involves a tail call, which is where the recursive call is the last statement of the function.

Parallelism

One area where the use of functional programming can be useful is handling parallel, also called concurrent, programming tasks. Consider the following sequence:

result = a.methodA() + b.methodB() + c.methodC();

In what order can these methods be executed? If they have side effects, then they will most likely need to be computed sequentially. For example, the effect of methodA may affect the results of the other methods. However, if they do not have side effects, then the order of execution is not important and can be executed concurrently. Conceivably, they might not be executed at all until the value of result is needed, if ever. This is another potential application of lazy evaluation.

Java has steadily improved its support of concurrent programming over the years. These approaches built upon the underlying Thread class and provided various classes to support specific concurrent task such as pools.

The problem with these earlier approaches has been the need to learn these models and decide if they are a good fit for the problem at hand. While this is necessary and works well for many problem areas, it does require more effort on the part of the developer to learn these techniques.

In Java 8, much of the effort requires to add concurrent behavior to a program has been lessened allowing the developer to focus more on the problem at hand. This support comes in the use of functions in conjunction with streams and collections.

For example, the next code sequence illustrates how a lambda expression can be applied to each member of a stream. The Stream class's of method will generate a stream of integers. The map function applies the lambda expression, x->x*2, to each element of the stream:

    Stream<Integer> stream = Stream.of(12, 52, 32, 74, 25);
    stream.map(x->x*2)
        .forEach(x ->System.out.println(x));

The output follows:

24
104
64
148
50

This can be parallelized easily using the parallel method as shown here:

    stream = Stream.of(12, 52, 32, 74, 25);
    stream.parallel().map(x->x*2)
        .forEach(x ->System.out.println(x));

One possible output follows. However, since the stream operations are executed in parallel, a different output ordering is possible:

64
148
50
104
24

When the lambda expression is executed concurrently on different elements of the stream, the operations can be assigned to different processors and at different times. There is no guarantee with regard to the order in which the operations will be executed.

Humans are not very adept at multitasking, let alone writing concurrent programs that are reliable. By moving some of the decision-making process to the compiler and runtime system, more capable and efficient programs can be created.

Optional and monads

Null pointer exceptions are common, and their very existence is problematic to many developers. They introduce a slew of problems, including the need to handle them gracefully. The Optional class has been introduced in Java 8 to help deal with null pointer exceptions. It helps preserve type safety. The approach will ease the use of functions, provide an opportunity for using fluent interfaces, and avoid exception handling boilerplate code.

The intent of the Optional class is to help programmers deal with situations where a failure may occur. One way of handling this type of problem has been to return a null reference indicating a missing value. Using the Optional class forces the programmer to explicitly deal with the possibility that a function might not return a value. The Optional type should be used as the return type of a method or function that might not return a value.

Consider the situation where we would like to return an instance of a Customer class based on an ID using the following method:

    public Optional<Customer>findCustomerWithID(long id) {
        //...
        return someValue;
    }

Later when we invoke the function, a value of the Optional<Customer> type will be returned. We need to use the isPresent method to explicitly determine if a value is returned. If it is present, then the get method returns the actual Customer instance as shown next:

    Optional<Customer>optionalCustomer = findCustomerWithID(123);
    if (optionalCustomer.isPresent()) {
        Customer customer = optionalCustomer.get();
        // Use customer
    } else {
        // handle missing value
    }

The problem with simply returning null is that the programmer may not realize that a method may return null and may not attempt to handle it. This will result in a null pointer exception. In this example, since the findCustomerWithID method explicitly used the Optional type, we know and must deal with the possibility that nothing may be returned.

The Optional type allows chained function calls where a method might not return a value. We will demonstrate this in Chapter 6, Optional and Monads where the Optional type is discussed in more detail.

The Optional type has a monadic structure. A monad is basically a structure containing a set of computations represented as a series of steps. These computations are chained together effectively forming a pipeline. However, there is more to monads than this. Monads are a very useful technique and promote more reliable programs than most imperative programming techniques are capable of doing. You will learn more about the nature of monads and how to use them in Chapter 6, Optional and Monads.

In the same way, as you need to choose the right hammer for a job, you also need to choose the right language and programming style for the programming task. We don't want to use a sledge hammer to put a small nail in the wall for a picture. Since most jobs consist of multiple tasks, we need to use the right programming style for the specific task at hand.

Hence, a major focus of the book is how to blend the various programming styles available in Java 8 to meet an application's need. To be able to decide which technique is best for a given job, one needs to understand the nature of the task and how a technique supports such a task.

The incorporation of these functional programming techniques does not make Java a functional programming language. It means that we now have a new set of tools that we can use to solve the programming problems presented to us. It behooves us to take advantage of these techniques whenever they are applicable.

Java 8's support for functional style programming

So, what is the foundation for functional style programming in Java 8? Well, it comes from a number of additions and modifications to the language. In this section, we will briefly introduce several concepts that Java 8 uses. These include:

  • Lambda expressions
  • Default methods
  • Functional interfaces
  • Method and constructor references
  • Collections

Understanding these concepts will enable you to understand their purpose and why they are used.

Lambda expressions

Lambda expressions are essentially anonymous functions. They can be considered to be one of the most significant additions to Java 8. They can make the code easier to write and read.

We have already seen a lambda expression in the previous examples. In this section, we will provide additional detail about their form and use. There are three key aspects to lambda expressions:

  • They are a block of code
  • They may be passed parameters
  • They may return a value

The following table illustrates several different forms a simple lambda expression can take:

Lambda expression

Meaning

()->System.out.println()

It takes no arguments and displays a single line

x->System.out.println(x)

It takes a single argument and displays it on a line

x->2*x

It takes a single argument and returns its double

(x,y)->x+y

It takes two arguments and returns their sum

x -> {
int y = 2*x;
    return y;
}

It takes a single argument and returns its double using multiple statements

These examples are intended to provide some indication of what forms they may take on. A lambda expression may have zero, one, or more parameters and may return a value. They can be a concise single-line lambda expression or may consist of multiple lines. However, they need to be used in some context to be useful.

You can use a lambda expression in most places where a block of code needs to be executed. The advantage is that you do not need to formally declare and use a method to perform some task.

Lambda expressions are often converted to a functional interface automatically simplifying many tasks. Lambda expressions can access other variables outside of the expression. The ability to access these types of variables is an improvement over anonymous inner functions, which have problems in this regard. Lambda expressions will be discussed in more detail in Chapter 2, Putting the Function in Functional Programming.

Default methods

A default method is an interface method that possesses an implementation. Traditionally, interfaces can only contain abstract methods or static and final variables. This concept provides a way of defining a set of methods that a class can implement, and by doing so, it provides an enhanced form of polymorphic behavior.

Adding a default method to an interface is simple. The method is added using the default keyword along with its implementation. In the following example, an interface called Computable is declared. It has one abstract method and two default methods:

    public interface Computable {
        public int compute();

        public default int doubleNumber(int num) {
            return 2*num;
        }

        public default int negateNumber(int num) {
            return -1*num;
        }
    }

To use a default method, we create a class that implements its interface and executes the method against an instance of the new class. In the next sequence, the ComputeImpl class is declared that implements the Computable interface:

    public class ComputeImpl implements Computable {

        @Override
        public int compute() {
            return 1;
        } 
    }

Next, an instance of ComputeImpl is declared, and the default method is executed:

ComputeImplcomputeImpl  = new ComputeImpl();
System.out.println(computeImpl.doubleNumber(2));

The result will be a 4. We did not have to provide an implementation of the doubleNumber method in the ComputeImpl class before we used it. However, we can override if desired.

In Java 8, we can add default and static methods to interfaces. This has a number of advantages, including the ability to add capability to previous interfaces without breaking the existing code. This has allowed interfaces declared prior to Java 8 to be augmented with a default method that supports functional-type operations.

For example, the forEach method has been added as a default method to the java.lang package's Iterable interface. This method takes a lambda expression that matches the Consumer interface's accept method and executes it against each member of the underlying collection.

In the next code sequence, an array list is populated with three strings. The ArrayList class implements the Iterable interface enabling the use of the forEach method:

ArrayList<String> list = new ArrayList<>();
list.add("Apple");        
list.add("Peach");
list.add("Banana");
list.forEach(f->System.out.println(f));

The addition of a default method will not break code that was developed before the method was added.

Functional interfaces

A functional interface is an interface that has one and only one abstract method. The Computable interface declared in the previous section is a functional interface. It has one and only one abstract method: compute. If a second abstract method was added, the interface would no longer be a functional interface.

Functional interfaces facilitate the use of lambda expressions. This is illustrated with the Iterable interface's forEach method. It expects a lambda expression that implements the Consumer interface. This interface has a single abstract method, accept, making it a functional interface.

This means that the forEach method will accept any lambda expression that matches the accept method's signature as defined here:

    void accept(T t)

That is, it will use any lambda expression that is passed a single value and returns void. As seen with the ArrayList class used in the previous section and duplicated next, the lambda expression matches the signature of the accept method.

    list.forEach(f->System.out.println(f));

This is possible because Java 8 uses a technique called type inference to determine if the lambda expression can be used.

Java 8 has introduced a number of functional interfaces. However, conceptually they have been present in earlier version of Java, but were not identified as functional interfaces. For example, the Runnable interface, with its single abstract run method, is a functional interface. It has been a part of Java since the very beginning, but until Java 8 was not labeled as a functional interface.

The advantage of functional interfaces is that Java is able to automatically use a lambda expression that matches the signature of the abstract method found in a functional interface. Consider the creation of a thread as illustrated in the following code sequence:

    new Thread(()-> {
        for(inti=0; i<5; i++) {
            System.out.println("Thread!");
        }
    }).start();

The argument of the Thread class's constructor is a lambda expression that implements the Runnable interface's run method. This method takes zero argument and returns void. The lambda expression used matches this signature.

Method and constructor references

A method or constructor reference is a technique that allows a Java 8 programmer to use a method or constructor as if it was a lambda expression. In the following sequence, a stream is generated, its elements are doubled, and then displayed using a lambda expression:

    Stream<Integer> stream = Stream.of(12, 52, 32, 74, 25);
    Stream
        .map(x -> x * 2)
        .forEach(x ->System.out.println(x));

The output follows:

24
104
64
148
50

We can duplicate this sequence using a method reference in place of the lambda expression as shown next. A method reference takes the form of a class name followed by a double colon and then the method name. The parameter is implied, and the code will produce the same output as the previous example.

    Stream<Integer> stream = Stream.of(12, 52, 32, 74, 25);
    Stream
        .map(x -> x * 2) 
        .forEach(System.out::println);

In the following example, two method references are used where the first one invokes the sin method against each element of the list:

    stream
        .map(Math::sin) 
        .forEach(System.out::println);

The output follows:

-0.5365729180004349
0.9866275920404853
0.5514266812416906
-0.9851462604682474
-0.13235175009777303

We can also use constructors in a similar manner. Method and constructor references provide a convenient and easy way of using methods and constructors where lambda expressions are used.

Collections

The Collection interface has been enhanced in Java with the addition of methods that return a Stream object based on the collection. The stream method returns a stream executed in sequential order while the parallelStream method returns a stream that is executed concurrently. The following example illustrates the use of the stream method as applied against the list object. The List interface extends the Collection interface:

    String names[] = {"Sally", "George", "Paul"};
    List<String> list = Arrays.asList(names);
    Stream<String> stream = list.stream();
    stream.forEach(name ->System.out.println(name + " - " 
        + name.length()));

This sequence output follows:

Sally - 5
George - 6
Paul - 4

In addition, since the Collection interface inherits the Iterable interface's forEach method from the iterator. We can use this with the previous List object:

    list.forEach(name ->System.out.println(name + " - " 
        + name.length()));

There are other enhancements to collections in Java 8, which we will present as they are encountered.

Summary

In this chapter, we introduced many of the features that constitute a functional programming language. These included functions and the idea that they can be combined in more powerful ways than are possible in an imperative type language.

Functional languages frequently allow the expression of program logic using a fluent style where function invocations build upon each other. The expression of parallel behavior is simplified in functional programming languages allowing better optimization of code.

An important goal of functional programs has been to minimize the use of mutable data and avoid side effects. This also promotes certain optimizations and makes functional code more maintainable. Recursion is central to functional programming languages, and we hinted at how it can be used. The use of optional types and monads were also introduced.

Java 8 introduced several new language features that support the use of functions. These include lambda expressions, which underlie functions and functional interfaces with type inferences. The introduction of default methods enables the newer functional techniques to be used with older interfaces and classes. Method and constructor references provide a way of using these constructs where lambda expressions are expected.

With many of these topics, we provided simple examples of how Java can support these concepts. The remainder of the book provides a much more detailed discussion of how Java can be used.

Java is not a pure functional programming language. However, it supports many functional style techniques, which a knowledgeable developer can use. The use of these techniques require a different way of thinking about and approaching problems. We will convey these techniques in this book starting with a more detailed discussion of functions in Chapter 2, Putting the Function in Functional Programming.

Left arrow icon Right arrow icon

Key benefits

  • • Explore how you can blend object-oriented and functional programming styles in Java
  • • Use lambda expressions to write flexible and succinct code
  • • A tutorial that strengthens your fundamentals in functional programming techniques to enhance your applications

Description

Functional programming is an increasingly popular technology that allows you to simplify many tasks that are often cumbersome and awkward using an object-oriented approach. It is important to understand this approach and know how and when to apply it. Functional programming requires a different mindset, but once mastered it can be very rewarding. This book simplifies the learning process as a problem is described followed by its implementation using an object-oriented approach and then a solution is provided using appropriate functional programming techniques. Writing succinct and maintainable code is facilitated by many functional programming techniques including lambda expressions and streams. In this book, you will see numerous examples of how these techniques can be applied starting with an introduction to lambda expressions. Next, you will see how they can replace older approaches and be combined to achieve surprisingly elegant solutions to problems. This is followed by the investigation of related concepts such as the Optional class and monads, which offer an additional approach to handle problems. Design patterns have been instrumental in solving common problems. You will learn how these are enhanced with functional techniques. To transition from an object-oriented approach to a functional one, it is useful to have IDE support. IDE tools to refactor, debug, and test functional programs are demonstrated through the chapters. The end of the book brings together many of these functional programming techniques to create a more comprehensive application. You will find this book a very useful resource to learn and apply functional programming techniques in Java.

Who is this book for?

If you are a Java developer with object-oriented experience and want to use a functional programming approach in your applications, then this book is for you. All you need to get started is familiarity with basic Java object-oriented programming concepts.

What you will learn

  • • Use lambda expressions to simplyfy code
  • • Use function composition to achieve code fluency
  • • Apply streams to simply implementations and achieve parallelism
  • • Incorporate recursion to support an application's functionality
  • • Provide more robust implementations using Optionals
  • • Implement design patterns with less code
  • • Refactor object-oriented code to create a functional solution
  • • Use debugging and testing techniques specific to functional programs
Estimated delivery fee Deliver to Colombia

Standard delivery 10 - 13 business days

$19.95

Premium delivery 3 - 6 business days

$40.95
(Includes tracking information)

Product Details

Country selected
Publication date, Length, Edition, Language, ISBN-13
Publication date : Oct 14, 2015
Length: 296 pages
Edition : 1st
Language : English
ISBN-13 : 9781783558483
Category :
Languages :

What do you get with Print?

Product feature icon Instant access to your digital eBook copy whilst your Print order is Shipped
Product feature icon Paperback book shipped to your preferred address
Product feature icon Download this book in EPUB and PDF formats
Product feature icon Access this title in our online reader with advanced features
Product feature icon DRM FREE - Read whenever, wherever and however you want
OR
Modal Close icon
Payment Processing...
tick Completed

Shipping Address

Billing Address

Shipping Methods
Estimated delivery fee Deliver to Colombia

Standard delivery 10 - 13 business days

$19.95

Premium delivery 3 - 6 business days

$40.95
(Includes tracking information)

Product Details

Publication date : Oct 14, 2015
Length: 296 pages
Edition : 1st
Language : English
ISBN-13 : 9781783558483
Category :
Languages :

Packt Subscriptions

See our plans and pricing
Modal Close icon
$19.99 billed monthly
Feature tick icon Unlimited access to Packt's library of 7,000+ practical books and videos
Feature tick icon Constantly refreshed with 50+ new titles a month
Feature tick icon Exclusive Early access to books as they're written
Feature tick icon Solve problems while you work with advanced search and reference features
Feature tick icon Offline reading on the mobile app
Feature tick icon Simple pricing, no contract
$199.99 billed annually
Feature tick icon Unlimited access to Packt's library of 7,000+ practical books and videos
Feature tick icon Constantly refreshed with 50+ new titles a month
Feature tick icon Exclusive Early access to books as they're written
Feature tick icon Solve problems while you work with advanced search and reference features
Feature tick icon Offline reading on the mobile app
Feature tick icon Choose a DRM-free eBook or Video every month to keep
Feature tick icon PLUS own as many other DRM-free eBooks or Videos as you like for just $5 each
Feature tick icon Exclusive print discounts
$279.99 billed in 18 months
Feature tick icon Unlimited access to Packt's library of 7,000+ practical books and videos
Feature tick icon Constantly refreshed with 50+ new titles a month
Feature tick icon Exclusive Early access to books as they're written
Feature tick icon Solve problems while you work with advanced search and reference features
Feature tick icon Offline reading on the mobile app
Feature tick icon Choose a DRM-free eBook or Video every month to keep
Feature tick icon PLUS own as many other DRM-free eBooks or Videos as you like for just $5 each
Feature tick icon Exclusive print discounts

Frequently bought together


Stars icon
Total $ 170.97
Learning Java Functional Programming
$60.99
Mastering Concurrency Programming with Java 8
$60.99
Learning Reactive Programming With Java 8
$48.99
Total $ 170.97 Stars icon
Banner background image

Table of Contents

10 Chapters
1. Getting Started with Functional Programming Chevron down icon Chevron up icon
2. Putting the Function in Functional Programming Chevron down icon Chevron up icon
3. Function Composition and Fluent Interfaces Chevron down icon Chevron up icon
4. Streams and the Evaluation of Expressions Chevron down icon Chevron up icon
5. Recursion Techniques in Java 8 Chevron down icon Chevron up icon
6. Optional and Monads Chevron down icon Chevron up icon
7. Supporting Design Patterns Using Functional Programming Chevron down icon Chevron up icon
8. Refactoring, Debugging, and Testing Chevron down icon Chevron up icon
9. Bringing It All Together Chevron down icon Chevron up icon
Index Chevron down icon Chevron up icon

Customer reviews

Rating distribution
Full star icon Full star icon Full star icon Empty star icon Empty star icon 3
(1 Ratings)
5 star 0%
4 star 0%
3 star 100%
2 star 0%
1 star 0%
Waldorf Jan 09, 2016
Full star icon Full star icon Full star icon Empty star icon Empty star icon 3
I've been reading the Safari Online version of this book and it contains a lot of useful information. I would have given the book 4 stars but for the fact that the book has many, many typos, which is not good for a technical book. There are minor mistakes like misspellings and not matching the plurality of subjects and verbs, but there are also more important mistakes, such as errors in the code examples in the book and in the specifications of classes and interfaces. For example, in Chapter 2, where functional interfaces are discussed, the book says that the functional method in the BinaryOperator<T> interface has return type R, when it should have said T. Similarly, the book says that the getAsInt( ) method in the IntSupplier interface has return type R, when it should have said int. This book was not sufficiently proofread before publication.
Amazon Verified review Amazon
Get free access to Packt library with over 7500+ books and video courses for 7 days!
Start Free Trial

FAQs

What is the delivery time and cost of print book? Chevron down icon Chevron up icon

Shipping Details

USA:

'

Economy: Delivery to most addresses in the US within 10-15 business days

Premium: Trackable Delivery to most addresses in the US within 3-8 business days

UK:

Economy: Delivery to most addresses in the U.K. within 7-9 business days.
Shipments are not trackable

Premium: Trackable delivery to most addresses in the U.K. within 3-4 business days!
Add one extra business day for deliveries to Northern Ireland and Scottish Highlands and islands

EU:

Premium: Trackable delivery to most EU destinations within 4-9 business days.

Australia:

Economy: Can deliver to P. O. Boxes and private residences.
Trackable service with delivery to addresses in Australia only.
Delivery time ranges from 7-9 business days for VIC and 8-10 business days for Interstate metro
Delivery time is up to 15 business days for remote areas of WA, NT & QLD.

Premium: Delivery to addresses in Australia only
Trackable delivery to most P. O. Boxes and private residences in Australia within 4-5 days based on the distance to a destination following dispatch.

India:

Premium: Delivery to most Indian addresses within 5-6 business days

Rest of the World:

Premium: Countries in the American continent: Trackable delivery to most countries within 4-7 business days

Asia:

Premium: Delivery to most Asian addresses within 5-9 business days

Disclaimer:
All orders received before 5 PM U.K time would start printing from the next business day. So the estimated delivery times start from the next day as well. Orders received after 5 PM U.K time (in our internal systems) on a business day or anytime on the weekend will begin printing the second to next business day. For example, an order placed at 11 AM today will begin printing tomorrow, whereas an order placed at 9 PM tonight will begin printing the day after tomorrow.


Unfortunately, due to several restrictions, we are unable to ship to the following countries:

  1. Afghanistan
  2. American Samoa
  3. Belarus
  4. Brunei Darussalam
  5. Central African Republic
  6. The Democratic Republic of Congo
  7. Eritrea
  8. Guinea-bissau
  9. Iran
  10. Lebanon
  11. Libiya Arab Jamahriya
  12. Somalia
  13. Sudan
  14. Russian Federation
  15. Syrian Arab Republic
  16. Ukraine
  17. Venezuela
What is custom duty/charge? Chevron down icon Chevron up icon

Customs duty are charges levied on goods when they cross international borders. It is a tax that is imposed on imported goods. These duties are charged by special authorities and bodies created by local governments and are meant to protect local industries, economies, and businesses.

Do I have to pay customs charges for the print book order? Chevron down icon Chevron up icon

The orders shipped to the countries that are listed under EU27 will not bear custom charges. They are paid by Packt as part of the order.

List of EU27 countries: www.gov.uk/eu-eea:

A custom duty or localized taxes may be applicable on the shipment and would be charged by the recipient country outside of the EU27 which should be paid by the customer and these duties are not included in the shipping charges been charged on the order.

How do I know my custom duty charges? Chevron down icon Chevron up icon

The amount of duty payable varies greatly depending on the imported goods, the country of origin and several other factors like the total invoice amount or dimensions like weight, and other such criteria applicable in your country.

For example:

  • If you live in Mexico, and the declared value of your ordered items is over $ 50, for you to receive a package, you will have to pay additional import tax of 19% which will be $ 9.50 to the courier service.
  • Whereas if you live in Turkey, and the declared value of your ordered items is over € 22, for you to receive a package, you will have to pay additional import tax of 18% which will be € 3.96 to the courier service.
How can I cancel my order? Chevron down icon Chevron up icon

Cancellation Policy for Published Printed Books:

You can cancel any order within 1 hour of placing the order. Simply contact customercare@packt.com with your order details or payment transaction id. If your order has already started the shipment process, we will do our best to stop it. However, if it is already on the way to you then when you receive it, you can contact us at customercare@packt.com using the returns and refund process.

Please understand that Packt Publishing cannot provide refunds or cancel any order except for the cases described in our Return Policy (i.e. Packt Publishing agrees to replace your printed book because it arrives damaged or material defect in book), Packt Publishing will not accept returns.

What is your returns and refunds policy? Chevron down icon Chevron up icon

Return Policy:

We want you to be happy with your purchase from Packtpub.com. We will not hassle you with returning print books to us. If the print book you receive from us is incorrect, damaged, doesn't work or is unacceptably late, please contact Customer Relations Team on customercare@packt.com with the order number and issue details as explained below:

  1. If you ordered (eBook, Video or Print Book) incorrectly or accidentally, please contact Customer Relations Team on customercare@packt.com within one hour of placing the order and we will replace/refund you the item cost.
  2. Sadly, if your eBook or Video file is faulty or a fault occurs during the eBook or Video being made available to you, i.e. during download then you should contact Customer Relations Team within 14 days of purchase on customercare@packt.com who will be able to resolve this issue for you.
  3. You will have a choice of replacement or refund of the problem items.(damaged, defective or incorrect)
  4. Once Customer Care Team confirms that you will be refunded, you should receive the refund within 10 to 12 working days.
  5. If you are only requesting a refund of one book from a multiple order, then we will refund you the appropriate single item.
  6. Where the items were shipped under a free shipping offer, there will be no shipping costs to refund.

On the off chance your printed book arrives damaged, with book material defect, contact our Customer Relation Team on customercare@packt.com within 14 days of receipt of the book with appropriate evidence of damage and we will work with you to secure a replacement copy, if necessary. Please note that each printed book you order from us is individually made by Packt's professional book-printing partner which is on a print-on-demand basis.

What tax is charged? Chevron down icon Chevron up icon

Currently, no tax is charged on the purchase of any print book (subject to change based on the laws and regulations). A localized VAT fee is charged only to our European and UK customers on eBooks, Video and subscriptions that they buy. GST is charged to Indian customers for eBooks and video purchases.

What payment methods can I use? Chevron down icon Chevron up icon

You can pay with the following card types:

  1. Visa Debit
  2. Visa Credit
  3. MasterCard
  4. PayPal
What is the delivery time and cost of print books? Chevron down icon Chevron up icon

Shipping Details

USA:

'

Economy: Delivery to most addresses in the US within 10-15 business days

Premium: Trackable Delivery to most addresses in the US within 3-8 business days

UK:

Economy: Delivery to most addresses in the U.K. within 7-9 business days.
Shipments are not trackable

Premium: Trackable delivery to most addresses in the U.K. within 3-4 business days!
Add one extra business day for deliveries to Northern Ireland and Scottish Highlands and islands

EU:

Premium: Trackable delivery to most EU destinations within 4-9 business days.

Australia:

Economy: Can deliver to P. O. Boxes and private residences.
Trackable service with delivery to addresses in Australia only.
Delivery time ranges from 7-9 business days for VIC and 8-10 business days for Interstate metro
Delivery time is up to 15 business days for remote areas of WA, NT & QLD.

Premium: Delivery to addresses in Australia only
Trackable delivery to most P. O. Boxes and private residences in Australia within 4-5 days based on the distance to a destination following dispatch.

India:

Premium: Delivery to most Indian addresses within 5-6 business days

Rest of the World:

Premium: Countries in the American continent: Trackable delivery to most countries within 4-7 business days

Asia:

Premium: Delivery to most Asian addresses within 5-9 business days

Disclaimer:
All orders received before 5 PM U.K time would start printing from the next business day. So the estimated delivery times start from the next day as well. Orders received after 5 PM U.K time (in our internal systems) on a business day or anytime on the weekend will begin printing the second to next business day. For example, an order placed at 11 AM today will begin printing tomorrow, whereas an order placed at 9 PM tonight will begin printing the day after tomorrow.


Unfortunately, due to several restrictions, we are unable to ship to the following countries:

  1. Afghanistan
  2. American Samoa
  3. Belarus
  4. Brunei Darussalam
  5. Central African Republic
  6. The Democratic Republic of Congo
  7. Eritrea
  8. Guinea-bissau
  9. Iran
  10. Lebanon
  11. Libiya Arab Jamahriya
  12. Somalia
  13. Sudan
  14. Russian Federation
  15. Syrian Arab Republic
  16. Ukraine
  17. Venezuela