Chapter 2. Using the Functional Constructions of Java 8

Functional programming is not a new idea; actually, it's pretty old. For example, Lisp, which is a functional language, is the second oldest of today's commonly-used programming languages.

Functional programs are built using small pieces of reusable pure functions (lambdas). The program logic is composed of small declarative steps and not complex algorithms. That's because functional programs minimize the use of state, which makes imperative programs complex and hard to refactor/support.

With Java 8, the Java world got the lambda expressions and the ability to pass functions to functions. With them, we can code in a more functional style and get rid of a lot of the boilerplate code. The other new thing we got with Java 8 is the streams—something very similar to RxJava's observables but not asynchronous. Combining these streams and the lambdas, we are able to create more functional-like programs.

We are going to familiarize ourselves with these new constructions and look at how they can be used with RxJava's abstractions. Our programs will be simpler and easier to follow by using the lambdas, and the concepts introduced in this chapter will be of help while designing applications.

This chapter covers:

  • Lambdas in Java 8
  • First RxJava examples using the lambda syntax
  • What pure functions and higher order functions are

Lambdas in Java 8

The most important change in Java 8 is the introduction of lambda expressions. They enable faster, clearer coding and make it possible to use functional programming.

Java was created back in the '90s as an object-oriented programming language, with the idea that everything should be an object. At that time, object-oriented programming was the principal paradigm for software development. But, recently, functional programming has become increasingly popular because it is well-suited for concurrent and event-driven programming. This doesn't mean that we should stop writing code using object-oriented languages. Instead, the best strategy is to mix elements of object-oriented and functional programming. Adding lambdas to Java 8 ties in with this idea—Java is an object-oriented language, but now it has lambdas, we are able to code using the functional style too.

Let's look at this new feature in detail.

Introducing the new syntax and semantics

In order to introduce lambda expressions, we need to see their actual value. This is why this chapter will begin with one example implemented without using lambda expressions, followed by re-implementing the same example using lambda expressions.

Remember the map(Func1) method from the Observable class? Let's try to implement something similar for the java.util.List collections. Of course, Java doesn't support adding methods to existing classes, so the implementation will be a static method that takes a list and transformation and returns a new list containing the transformed elements. In order to pass a transformation to the method, we'll need an interface with one method representing it.

Let's look at the code:

interface Mapper<V, M> { // (1)
  M map(V value); // (2)
}

// (3)	
public static <V, M> List<M> map(List<V> list, Mapper<V, M> mapper) {
  List<M> mapped = new ArrayList<M>(list.size()); // (4)
  for (V v : list) {
    mapped.add(mapper.map(v)); // (5)
  }
  return mapped; // (6)
}

What is happening here?

  1. We define a generic interface, called Mapper.
  2. It has only one method, M map(V), that receives a value of type V and transforms it to a value of type M.
  3. The static method List<M> map(List<V>, Mapper<V, M>) takes one list with elements of type V and a Mapper implementation. Using this Mapper implementation's map() method on every element of the source list, it converts the list to a new list of type M containing the transformed elements.
  4. The implementation creates a new empty list of type M with the same size as the source list.
  5. Every element in the source list is transformed using the passed Mapper implementation and added to the new list.
  6. The new list is returned.

In this implementation, every time we want to create a new list by transforming another, we will have to implement the Mapper interface with the right transformation. Until Java 8, the right way of passing custom logic to methods was exactly like this—with anonymous class instances, implementing the given methods.

But let's look at how we use this List<M> map(List<V>, Mapper<V, M>) method:

List<Integer> mapped = map(numbers, new Mapper<Integer, Integer>() {
  @Override
  public Integer map(Integer value) {
    return value * value; // actual mapping
  }
});

In order to apply a mapping to a list, we need to write four lines of boilerplate code. The actual mapping is very simple and is only one of these lines. The real problem here is that instead of passing an action, we are passing an object. This obscures the real intention of this program—to pass an action that produces transformation from every item of the source list and to get a list with applied changes at the end.

Here is what this call looks like using the new lambda syntax of Java 8:

List<Integer> mapped = map(numbers, value -> value * value);

Pretty straight forward, isn't it? And it just works. Instead of passing an object and implementing an interface, we pass a block of code, a nameless function.

What is going on? We defined an arbitrary interface with an arbitrary method, but we could pass this lambda in place of an instance of the interface. In Java 8, if you define interface with only one abstract method and you create a method that receives a parameter of this type of interface, you can pass a lambda instead. If the interface single method takes two arguments of type string and returns an integer value, the lambda will have to be composed of two arguments before the -> and to return integer, the arguments will be inferred as strings.

Interfaces of this type are called functional interfaces. It is important for the single method to be abstract and not default. Another new thing in Java 8 is the default methods of interfaces:

interface Program {
  default String fromChapter() {
    return "Two";
  }
}

The default methods are useful when changing already existing interfaces. When we add default methods to them, the classes implementing them won't break. An interface with only one default method is not functional; a single method shouldn't be default.

Lambdas act as implementations of the functional interfaces. So, it is possible to assign them to variables of type interface as follows:

Mapper<Integer, Integer> square = (value) -> value * value;

And we can reuse the square object as it's an implementation of the Mapper interface.

Maybe you've noticed, but in the examples up until now, the parameters of lambda expressions have no type. That is because the types are inferred. So this expression is absolutely the same as the preceding expression:

Mapper<Integer, Integer> square = (Integer value) -> value * value;

The fact that the example with a parameter without a type works is not magic. Java is a statically typed language, so the parameter of the single method of the functional interface is used for type checking.

How about the body of the lambda expression? There is no return statement anywhere. It turns out that these two examples are exactly the same:

Mapper<Integer, Integer> square = (value) -> value * value;
// and
Mapper<Integer, Integer> square = (value) -> {
  return value * value;
};

The first expression is just a short form of the second. It is preferred for the lambda to be only one line of code. But if the lambda expression contains more than one line, the only way to define it is using the second approach, like this:

Mapper<Integer, Integer> square = (value) -> {
  System.out.println("Calculating the square of " + value);
  return value * value;
};

Under the hood, lambda expressions are not just syntax sugar for anonymous inner classes. They are implemented to perform quickly inside the Java Virtual Machine (JVM), so if your code is designed to be compatible only with Java 8+, you should definitely use them. Their main idea is to pass around behavior in the same way that data is passed. This makes your program more human readable.

One last thing related to the new syntax is the ability to pass to methods and assign to variables already defined functions and methods. Let's define a new functional interface:

interface Action<V> {
  void act(V value);
}

We can use it to execute arbitrary actions for each value in a list; for example, logging the list. Here is a method that uses this interface:

public static <V> void act(List<V> list, Action<V> action) {
  for (V v : list) {
    action.act(v);
  }
}

This method is similar to the map() function. It iterates through the list and calls the passed action's act() method on every element. Let's call it using a lambda that simply logs the elements in the list:

act(list, value -> System.out.println(value));

This is quite simple but not necessary because the println() method can be passed itself to the act() method. This is done as follows:

act(list, System.out::println);

This is valid syntax in Java 8—every method can become a lambda and can be assigned to a variable or passed to a method. All these are valid:

  • Book::makeBook // Static method of a class
  • book::read // method of an instance
  • Book::new // Constructor of a class
  • Book::read // instance method, but referenced without using an actual instance.

Now that we've revealed the lambda syntax, we will be using it in our RxJava examples instead of anonymous inner classes.

Functional interfaces in Java 8 and RxJava

Java 8 comes with a special package containing functional interfaces for common cases. This package is java.util.function, and we are not going to look at it in detail in this book, but will present some of them that are worth mentioning:

  • Consumer<T>: This represents a function that accepts an argument and returns nothing. Its abstract method is void accept(T). As an example, we can use it to assign the System.out::println method to a variable, as follows:
    Consumer<String> print = System.out::println;
  • Function<T,R>: This represents a function that accepts one argument of a given type and returns a result of an arbitrary type. Its abstract method is R accept(T), and it can be used for mapping. We don't need the Mapper interface at all! Let's take a look at the following code snippet:
    Function<Integer, String> toStr = (value) -> (value + "!");
    List<String> string = map(integers, toStr);
  • Predicate<T>: This stands for a function with only one argument that returns a Boolean result. Its abstract method is boolean test(T) and it can be used for filtering. Let's take a look at the following code:
    Predicate<Integer> odd = (value) -> value % 2 != 0;

There are a lot of functional interfaces similar to these; for example, a function with two arguments, or a binary operator. This is again a function with two arguments, but both of the same type and returning a result with the same type. They are there to help reuse lambdas in our code.

The good thing is that RxJava is lambda compatible. This means that the actions we were passing to the subscribe method are in fact functional interfaces!

RxJava's functional interfaces are in the rx.functions package. All of them extend a base marker interface (interface with no methods, used for type checking), called Function. Additionally, there is another marker interface, extending the Function one, called Action. It is used to mark consumers (functions, returning nothing).

RxJava has eleven Action interfaces:

Action0 // Action with no parameters
Action1<T1> // Action with one parameter
Action2<T1,T2> // Action with two parameters
Action9<T1,T2,T3,T4,T5,T6,T7,T8,T9> // Action with nine parameters
ActionN // Action with arbitrary number of parameters

They can be used mainly for subscriptions (Action1 and Action0). The Observable.OnSubscribe<T> parameter, which we saw in Chapter 1, An Introduction to Reactive Programming, (used for creating custom observables) extends the Action interface too.

Analogically, there are eleven Function extenders representing function returning result. They are Func0<R>, Func1<T1, R>Func9<T1,T2,T3,T4,T5,T6,T7,T8,T9,R>, and FuncN<R>. They are used for mapping, filtering, combining, and many other purposes.

Every operator and subscribe method in RxJava is applicable to one or more of these interfaces. This means that we can use lambda expressions instead of anonymous inner classes in RxJava almost everywhere. From this point on, all our examples will use lambdas in order to be more readable and somewhat functional.

Now, let's look at one big RxJava example implemented with lambdas. This is our familiar Reactive Sum example!

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
3.137.220.92