Functional Programming Notes

These are notes from a course I purchased and followed.

What is functional programming?

A quick example...

Imperative example (traditional Java)

Functional example (Java 8 and beyond)

Comments

Output

Every instruction is written explicitly, uses loops and conditions.

import java.util.List;

public class ImperativeExample
{
  public static int calculateSum( List< Integer > numbers )
  {
    int sum = 0;

    for( int number : numbers )
      if( number % 2 == 0 )
        sum += number;

    return sum;
  }

Able to concentrate on what's needed rather than on how to do it.

import java.util.List;

class FunctionalExample
{
  public static int calculateSum( List< Integer > numbers )
  {


    return numbers.stream()
               .filter( number -> number % 2 == 0 )
               .mapToInt( number -> number )
               .sum();
  }
}

Step by step—what each does:

_







// for each/over the stream of numbers
// but, only even numbers, ...
// convert number to int
// and add to sum!

_

What is output from both:

 Test: test -----------------------------
 Numbers: [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]

 Test: testCalculateSum -----------------
 30
 





My test code...

In the interest of full disclosure, my test code looks like this:

import java.util.Arrays;
import java.util.List;

import org.junit.After;
import org.junit.Before;
import org.junit.FixMethodOrder;
import org.junit.Rule;
import org.junit.Test;
import org.junit.rules.TestName;
import org.junit.runners.MethodSorters;

import net.javaguides.ImperativeExample;

import com.windofkeltia.utilities.TestUtilities;

/**
 * @author Russell Bateman
 * @since May 2025
 */
@FixMethodOrder( MethodSorters.JVM )
public class xExampleTest
{
  @Rule   public TestName name = new TestName();
  @After  public void     tearDown() { System.out.println(); }
  @Before public void     setUp()    { TestUtilities.setUp( name, 33 ); }

  private static final List< Integer > numbers = Arrays.asList( 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 );

  @Test public void test()             { System.out.println( "Numbers: " + numbers ); }
  @Test public void testCalculateSum() { System.out.println( ImperativeExample.calculateSum( numbers ) ); }
}
import org.junit.rules.TestName;

public class TestUtilities
{
  public static void setUp( TestName name, int pad )
  {
    String testName  = name.getMethodName();

    System.out.print( "Test: " + testName + " " );

    int    nameWidth = testName.length();
    pad -= nameWidth;
    while( pad-- > 0 )
      System.out.print( "-" );
    System.out.println();
  }
}









Pure functions

A pure function in functional programming follows two rules:

  1. It always produces the same output for the same input.
  2. There are no side effects; it does not modify external, variables, states or data.

Functions are first-class objects in functional programming

That is, if they are defined using Function< ?, ? > function.

In Java functional programming, it's possible to return first-class functions as objects in a variable or in a function return. In this, Java becomes a bit more like C.

First-class functions are created according to this example (which involves a lambda expression). Applying the function is also shown:

Function< Integer, Integer > square = x -> x * x;

System.out.println( "The square of 5 is %d.", square.apply( 5 ) );

Just as in C, functions in Java are treated like variables in that they can be assigned to a variable, get passed as arguments to other functions and get returned from other functions.

Higher-order functions

A higher-order function is one that can take another function as an argument and/or returns a function as its result. Here are two examples:

import java.util.Arrays;
import java.util.List;
import java.util.function.Function;
import java.util.stream.Collectors;

public class HighOrderFunctionsTest
{
  static void applyFunction( Function< Integer, Integer > function, int value )
  {
    System.out.println( "Result: " + function.apply( value ) );
  }

  static Function< Integer, Integer > createMultiplier( int factor )
  {
    return x -> x * factor;
  }

  static List< Integer > applyFunctionToList( List< Integer > numbers, Function< Integer, Integer > function )
  {
    return numbers.stream()
               .map( function )                  // (apply function to each element)
               .collect( Collectors.toList() );
  }

  static void main( String[] args )
  {
    List< Integer >              numbers        = Arrays.asList( 1, 2, 3, 4, 5 );
    Function< Integer, Integer > doubleValue    = x -> x * 2;
    List< Integer >              doubledNumbers = applyFunctionToList( numbers, doubleValue );

    System.out.println( "Doubled numbers: " + doubledNumbers );
  }
}

Asserted advantages of higher-order functions are:

Rules of pure functional programming

Pure functional programming follows strict rules to ensure that functions are predictable, resusable and maintainable. There must be...

Lambda expressions

A lambda expression is simply a function without a name, also called an anonymous function. It can be used as an argument to function where a function is expected. Lambda expressions facilitate functional programming and simplify development.

Principally, the purpose of the lambda expression is to provide an implementation for functional interfaces.

Remember that a functional interface is one that contains exactly one abstract method (though it may also contain default and static methods).

The lambda expression is constructed syntactically (lambda expression syntax) as:

 parameters  linking to  lambda body

( arguments )   ->        statement
                or
( arguments )   ->      { statements }

Example

First, the object-oriented way (later to be compared to the functional programming way):

public class ShapeTest
{
interface Shape
{
  void draw();
}

class Rectangle implements Shape
{
  @Override
  public void draw() { System.out.println( "Rectangle drawn" ); }
}

class Square implements Shape
{
  @Override
  public void draw() { System.out.println( "Square drawn" ); }
}

class Circle implements Shape
{
  @Override
  public void draw() { System.out.println( "Circle drawn" ); }
}

Shape rectangle = new Rectangle();
Shape square    = new Square();
Shape circle    = new Circle();

public static void main( String[] args )
{
  rectangle.draw();
  square.draw();
  circle.draw();
}
}

The functional-programming way:

public class ShapeTest
{
  interface Shape
  {
    void draw();
  }

  // (remember: lambda is arguments (none) -> (one) statement
  static Shape rectangle = () -> System.out.println( "Rectangle drawn" );
  static Shape square    = () -> System.out.println( "Square drawn" );
  static Shape circle    = () -> System.out.println( "Circle drawn" );

  public static void main( String[] args )
  {
    rectangle.draw();
    square.draw();
    circle.draw();
  }
}

Notes

As we transition (the OOP code) to functional programming, we change some things. It's as if we copy down the Java code:

class Rectangle implements Shape1
{
  @Override2
  public3 void4 draw5() { System.out.println( "Rectangle drawn" ); }
}

...and convert it over to functional:

  1. Shape is the interface-derived object we're going to create.
  2. @Override is an OOP concept that is meaningless in functional programming.
  3. public is an OOP concept that has no place in functional programming.
  4. void: we don't need the return type of the draw() function because Java knows what it is.
  5. draw: we already told Java that Shape has abstract method, draw() so we don't need to tell it; besides, lambda expressions do not have a name.
  6. link the parameter into the lambda body using operator ->.
  7. The (sole) implementation statement is System.out.println(). We won't need braces.

Calculator example

public class CalculatorTest
{
  @FunctionalInterface   // (unnecessary, but good practice to use)
  interface Calculator
  {
    int calculate( int a, int b );
  }

  static class CalculatorExample
  {
    public static void main( String[] args )
    {
  //  imagine all the class boilerplate to be created above (missing here)
  //  in support of what's below:
  //  Calculator addition = new Addition;
  //  System.out.println( addition.calculate( 10, 20 ) );
  //
  //  Calculator subtraction = new Subtraction;
  //  System.out.println( subtraction.calculate( 20, 10 ) );
  //
  //  Calculator multiplication = new Multiplication;
  //  System.out.println( multiplication.calculate( 10, 20 ) );
  //
  //  Calculator division = new Division;
  //  System.out.println( division.calculate( 20, 10 ) );

      // instead, let's use lambda expressions:
      Calculator addition = ( a, b ) -> a + b;
      System.out.println( addition.calculate( 10, 20 ) );

      Calculator subtraction = ( a, b ) -> a - b;
      System.out.println( subtraction.calculate( 20, 10 ) );

      Calculator multiplication = ( a, b ) -> a * b;
      System.out.println( multiplication.calculate( 10, 20 ) );

      Calculator division = ( a, b ) -> a / b;
      System.out.println( division.calculate( 20, 10 ) );
    }
  }
}

Passing lambda expression as a method argument

We're referring to the calculator code above.

public class CalculatorTest
{
  interface Calculator
  {
    int calculate( int a, int b );
  }

  static class CalculatorExample
  {
    public static void main( String[] args )
    {
      // replace what's above with these passing the lambda as third argument:
      System.out.println( calculate( 10, 20, ( a, b ) -> a + b ) );
      System.out.println( calculate( 20, 10, ( a, b ) -> a - b ) );
      System.out.println( calculate( 10, 20, ( a, b ) -> a * b ) );
      System.out.println( calculate( 20, 10, ( a, b ) -> a / b ) );
    }

    // but, we have to create this method:
    private static int calculate( int a, int b, Calculator calculator )
    {
      return calculator.calculate( a, b );
    }
  }
}

Using lambda expression for Runnable

class ThreadDemo implements Runnable
{
  @Override
  public void run()
  {
    System.out.println( "run() method called..." );
  }
}

public class LambdaThread
{

  public static void main( String[] args )
  {
    ThreadDemo thread = new Thread( new ThreadDemo() );
    thread.start();
  }
}

Sort objects using lambda in ascending and descending order

Eschewing the traditional Java boilerplate, let's do this using lambdas.

import java.util.ArrayList;
import java.util.Collections;
import java.util.List;

public class EmployeeTest
{
  static class Employee
  {
    private final int    id;
    private final String name;
    private final int    age;
    private       int    salary;

    Employee( int id, String name, int age, int salary )
    {
      this.id     = id;
      this.name   = name;
      this.age    = age;
      this.salary = salary;
    }

    public int getSalary() { return salary; }

    @Override
    public String toString()
    {
      return "{\n"
        + "      id: " + id     + '\n'
        + "    name: " + name   + '\n'
        + "     age: " + age    + '\n'
        + "  salary: " + salary + '\n'
        + "}";
    }
  }

  static class SortEmployeeList
  {
    public static void main( String[] args )
    {
      List< Employee > employees = new ArrayList<>();
      employees.add( new Employee( 23, "Russell", 70, 50000 ) );
      employees.add( new Employee(  1, "Bruce",   62, 80000 ) );
      employees.add( new Employee(  2, "Moray",   75, 75000 ) );

      System.out.println( "Ascending order by salary:" );

      Collections.sort( employees, ( a, b ) -> a.getSalary() - b.getSalary());
      for( Employee employee : employees )
        System.out.println( employee );

      System.out.println( "\nDescending order by salary:" );

      Collections.sort( employees, ( a, b ) -> b.getSalary() - a.getSalary());
      for( Employee employee : employees )
        System.out.println( employee );
    }
  }
}

Functional Interfaces in Java

Functional interfaces follow this pattern:

@FunctionalInterface
interface MyFunctionalInterface
{
        void abstractMethod();                   // single, abstract method
  default void defaultMethod() { System.out.println( "Default method" ); }
  static  void staticMethod()  { System.out.println( "Static method" ); }
}

Java 8 introduced several built-ins within the java.util.function package. Common ones include:

Let's try it:

package com.windofkeltia.interfaces;

@FunctionalInterface
interface Printable
{
  void print( String message );
}

public class PrintableDemo
{
  public static void main( String[] args )
  {
    // note that because only one parameter, no need for parentheses:
    Printable printable = message -> System.out.println( message );
    printable( "Hello world" );
  }
}

Built-in Functional Interfaces in Java

/**
 * @parm T type of the argument.
 * @parm R type of the result.
 */
interface Function< T, R >
{
  /** Applies this function to the given argument.
   * @param t the function argument.
   * @return the function result.
   */
  R apply( T t );    // takes argument type T and returns result type R

}

Let's demonstrate. First, an implementation according to traditional Java (highlighted). We'll use
an anonymous class implementation.

import java.util.function.Function;

public class FunctionDemo
{
  public static void main( String[] args )
  {
    Function< String, String > function = new Function<>()
    {
      @Override
      public String apply( String s )
      {
        return s.toUpperCase();
      }
    };

    System.out.println( function.apply( "Russell" ) );
  }
}

Now, let's recode using lambda—reducing the implementation to one line.

import java.util.function.Function;

public class FunctionDemo
{
  public static void main( String[] args )
  {
    Function< String, String > uppercase = s -> s.toUpperCase();

    System.out.println( uppercase.apply( "Russell" ) );
  }
}

Both print:

RUSSELL

Let's simply add another function to reverse the string.

import java.util.function.Function;

public class FunctionDemo
{
  public static void main( String[] args )
  {
    Function< String, String > uppercase = s -> s.toUpperCase();

    System.out.println( uppercase.apply( "Russell" ) );

    Function< String, String > reverse = s ->
    {
      return new StringBuilder( s ).reverse().toString();
    };

    System.out.println( reverse.apply( "Russell" ) );
  }
}

The output is:

RUSSELL
llessuR

Note also other variants on the code above, to wit for lines 7 and 11-14:

/*     7: */ Function< String, String > uppercase = String::toUpperCase;
/* 11-14: */ Function< String, String > reverse   = s -> new StringBuilder( s ).reverse().toString();