How to make functional programming in Java easier

Consider declarative use of functional programming in Java coding.


Java developers have become accustomed to using the programming paradigm of imperative and object-oriented approaches because the Java language naturally supports these features. In Java 8, we have access to a number of new and more powerful features and syntax. Functional programming has been around for decades and is generally cleaner, more expressive, less error-prone, and easier to parallelize than object-oriented programming. Therefore, there is a strong case for introducing functional programming features into Java programs. However, programming in a functional style requires some changes to the existing code design.

About the series

Java 8 is the most significant update to the language since its inception — it includes richer new features. Venkat Subramaniam, an educator and author of this series, offers a common approach to Java 8: a short exploration that allows you to rethink Java conventions that are taken for granted, while also thinking about how to gradually integrate new technologies and syntax into your programs.

I’ve found it easier to transition to this more functional programming style by thinking declaratively rather than imperative when coding. In this first article in the Java 8 Idioms Series, I explained the differences and common ground between imperative, declarative, and functional programming. Now I’ll show you how to use declarative thinking to apply functional programming to your actual programming.

Imperative style

Developers accustomed to the imperative style of programming like to tell programs what to do and how to do it. Here’s a simple example:

Directory 1: Implement findNemo in an imperative style

import java.util.*;

public class FindNemo {
  public static void main(String[] args) {
    List<String> names = 
      Arrays.asList("Dory"."Gill"."Bruce"."Nemo"."Darla"."Marlin"."Jacques");

    findNemo(names);
  }                 
  
  public static void findNemo(List<String> names) {
    boolean found = false;
    for(String name : names) {
      if(name.equals("Nemo")) {
        found = true;
        break; }}if(found)
      System.out.println("Found Nemo");
    else
      System.out.println("Sorry, Nemo not found"); }}Copy the code

The findNemo() method first initializes an identifier variable found, also known as garbage. Some developers often call these variables f, t, or temp to indicate that they are only used to represent intermediate states that shouldn’t exist.

The program then iterates through each element of the NAMES list. It checks if these elements match Nemo, sets found to true if they do and breaks out of the loop,

In the imperative style that every Java developer is familiar with, you define each step of the program, including which elements to iterate over, how to compare, what to do when matching, and when to break out of the loop. An imperative style allows you to have absolute control over the program, which looks good. On the other hand, in many scenarios, you can reduce your workload by doing this.

Declarative style

With declarative programming, you still have to tell the program what to do, but the implementation details are left to the underlying library. Let’s rewrite the findNemo method above using declarative style:

Implement findNemo in a declarative style

 public static void findNemo(List<String> names) {
   if(names.contains("Nemo"))
     System.out.println("Found Nemo");
   else
     System.out.println("Sorry, Nemo not found");
 }
Copy the code

In contrast to the imperative style of the code, notice that there is no declaration of identity variables, and no loop is used to iterate over each element. On the contrary. We implement this function directly using the contains() method. In general, the implementation details are handed over to the underlying library, although you still need to tell the program what to do: check if the collection has the values we want.

In imperative code, you tell the program to do what you want by specifying a traversal, but in the declarative version, you don’t care how the implementation works, you just care what it returns, and it’s much easier to do what you want based on what it returns.

Finding yourself programming and thinking in a declarative style will greatly simplify your transition to functional programming. This is because functional programming is built on a declarative style, and declarative thinking provides the transition from imperative to functional programming.

Functional style

While the style of functional programming is always declarative, this does not mean that simply using declarative programming is functional programming. This is because functional programming also needs to be combined with higher-order function use. The following diagram illustrates the relationship between imperative, declarative, and functional programming.

Higher-order functions in Java

In Java, you can pass an object as an argument to a method, create an object in the method and return it, and you can do the same for a function: pass the function as an argument to the method, process the function in the method, and return the processed function.

In this case, the method is part of the class: a static variable or example, but the function passed in is just a local variable of the method and cannot be associated with the class itself or with an instance. Methods that receive, process, and return functions are called higher-order functions.

An example of functional programming

Using a new programming style in your programming will require you to change the way you think about programs, and you can use this simple example to explore how to build more complex programs with this style.

Directory 3: Imperative style maps

import java.util.*;

public class UseMap {
  public static void main(String[] args) {
    Map<String, Integer> pageVisits = new HashMap<>();            
    
    String page = "https://agiledeveloper.com";
    
    incrementPageVisit(pageVisits, page);
    incrementPageVisit(pageVisits, page);
    
    System.out.println(pageVisits.get(page));
  }
  
  public static void incrementPageVisit(Map<String, Integer> pageVisits, String page) {
    if(! pageVisits.containsKey(page)) { pageVisits.put(page,0);
    }
    
    pageVisits.put(page, pageVisits.get(page) + 1); }}Copy the code

In directory 3, the main function creates an instance of a HashMap object that holds the site and its corresponding number of visits. The incrementPageVisit() method is also used to increase the number of visits to a given page, which we’ll focus on next.

The incrementPageVisit() method is written in an imperative style: its job is to increase the count of a given page and store it in a Map. The method does not know if a count already exists for a given page, so it first checks if a count exists. If not, it inserts a “0” as a count for the page. It then takes the count, increments it, and stores the new value in the Map.

Declarative thinking requires you to shift from “how” to “why” when designing this approach. When the method incrementPageVisit() is called, you want to either initialize the count for a given page to 1 or increment its value. That’s “why it’s designed this way.”

The next step is to find a library method in the JDK library that can walk through the HashMap and do what we want to do.

Obviously, this library function is merge(). Directory 4 below uses a functional style to implement functions, but it is important to note that the merge method is a higher-order function and needs to be written in a different code style than above. This is a good example of functional style code.

Directory 4: Functional style maps

public static void incrementPageVisit(Map<String, Integer> pageVisits, String page) {
    pageVisits.merge(page, 1, (oldValue, value) > oldValue + value); }Copy the code

In directory 4, page is passed to merge() as the first argument: it serves as the key to update the value. The second argument is the default value for the key, which is created with the default value as its initial value when the key does not initially exist in the Map. The third argument is a lambda expression, which is a function that accepts old and new values. A lambda expression returns the sum of its arguments to increase the count of visits to a given web page. (Editor’s note: Thanks to Istvan Kovacs for pointing out and correcting errors in the code.)

Compare the single line of code in the incrementPageVisit() method in directory 4 to the multiple lines in directory 3. While the code in Directory 4 is a simple example of the functional style, the declarative programming mindset is more helpful.

conclusion

The idea of functional programming in Java programming has many benefits: the code is cleaner, more expressive, less modified, easier to parallelize, and its code is generally easier to understand than that of object-oriented programming. The challenge is to shift your thinking from the imperative programming style most developers are familiar with to a declarative one.

Writing functional style code isn’t easy, but in the process, your focus goes from “how the program works” to “how you want it to work.” By letting the underlying library manage your program’s execution for you, you will gradually and intuitively understand the role of higher-order functions at the heart of functional programming.