Java Function Programming
Tags: iss, beginners, java, functioninterface
Published: 2023-03-08 13:50:11

---
title: Java Function Programming
published: true
description: A study memo and record
tags: 'ISS, beginners, Java, FunctionInterface'
cover_image: null
canonical_url: null
id: 1283166
date: '2023-03-08T13:50:11Z'
---
*Contents:*

- [Re-think](#re-think)
  - [Openning Problem](#openning-problem)
  - [Opening Problem-Algorithm](#opening-problem-algorithm)
  - [A Java Solution](#a-java-solution)
  - [A LINQ Solution](#a-linq-solution)
- [Outline](#outline)
  - [Revisit – Defining a method](#revisit--defining-a-method)
  - [Lambda Expressions](#lambda-expressions)
    - [Syntax](#syntax)
    - [Implicit Parameter Types](#implicit-parameter-types)
    - [Implicit Return](#implicit-return)
    - [One Parameter](#one-parameter)
    - [Method Reference](#method-reference)
    - [Lambdas Summary](#lambdas-summary)
    - [Quiz](#quiz)
    - [More Practise](#more-practise)
  - [Function Interface](#function-interface)
    - [An example - Consumer](#an-example---consumer)
    - [Instance Instantiation](#instance-instantiation)
    - [Functional Interfaces and Lambdas](#functional-interfaces-and-lambdas)
    - [Using Functional Interfaces](#using-functional-interfaces)
      - [A Example -Pridicate](#a-example--pridicate)
      - [An example – BinaryOperator](#an-example--binaryoperator)
      - [Common Functional Interfaces](#common-functional-interfaces)
  - [Streams](#streams)
    - [Revisit - working with Collections](#revisit---working-with-collections)
    - [What are Streams?](#what-are-streams)
    - [Stream Pipelines](#stream-pipelines)
    - [Lazy vs eager operations](#lazy-vs-eager-operations)
    - [Stream Advantages](#stream-advantages)
    - [Creating Streams from Arrays](#creating-streams-from-arrays)
    - [Creating Streams from Collections](#creating-streams-from-collections)
    - [Creating ordered sequence of integer Streams](#creating-ordered-sequence-of-integer-streams)
    - [Common Intermediate Operations](#common-intermediate-operations)
    - [Filtering](#filtering)
    - [One way to understand a stream’s method](#one-way-to-understand-a-streams-method)
    - [Sorting](#sorting)
    - [Transforming](#transforming)
    - [Terminal Operations](#terminal-operations)
    - [“Iterating"](#iterating)

---

# Re-think

## Openning Problem

Given a list or array of *Person* objects

```java
public static List<Person> generatePersonList() {
    List<Person> l = new ArrayList<>();
    l.add(new Person("John", "Tan", 32, 2));
    l.add(new Person("Jessica", "Lim", 28, 3));
    l.add(new Person("Mary", "Lee", 42, 2));
    l.add(new Person("Jason", "Ng", 33, 1));
    l.add(new Person("Mike", "Ong", 22, 0));
    return l;
}
```

```java
class Person {
    private String firstName;
    private String lastName;
    private int age;
    private int kids;
    // Constructors, getters 
    // omitted for brevity
}

```

How can we print the **full names** of the **3 youngest** people?

## Opening Problem-Algorithm

1. Sort the list of people by age, in ascending order
2. Get the **first 3** people from the stored list
3. Get the respective **names**

## A Java Solution

```java
static void sortList(List<Person> persons) {
    for (int i = 0; i < persons.size(); i++) {
        for (int j = i + 1; j < persons.size(); j++) {
            if (persons.get(j).getAge() < persons.get(i).getAge()) {
                // Switch position of two persons
                Person temp = persons.get(i);
                persons.set(i, persons.get(j));
                persons.set(j, temp);
            }
        }
    }
}
```

1. Sort the list by age using Bubble SortNote: there’re many other ways to implement it.

    ```java
    static List<String> getTopThreeNames(
        List<Person> persons) {
        List<String> retNames = new ArrayList<String>();
        for (int i = 0; i < 3; i++) {
            Person curPerson = persons.get(i);
            retNames.add(curPerson.getFirstName() + " " + curPerson.getLastName());
        }
        return retNames;
    }
    ```

2. Get the first 3 people in the sorted list, and.
3. Get the respective names.

## A LINQ Solution

If working with C#, we can use **LINQ**

```java
Persons.OrderBy(person => person.Age)
    .Take(3)
    .Select(person => new
    {
        FullName = person.FirstName + " " + person.LastName
});
```

# Outline

## Revisit – Defining a method

A method definition consist of

1. Method name
2. Parameters
3. Return type
4. Method body
5. Return value

```java
int min(
    int num1, int num2){
        if(num1<num2){
            return num1;
        }
    return num2
}

```

How to make method definition **shorter** /more **concise** ?

## Lambda Expressions

A lambda expression represents an **anonymous methods**, in **short-hand** notation

it consists of:

1. ~~Method name~~
2. Parameters
3. ~~Return type~~
4. Method boday
5. Return value

```java

()->{}
Lambda Expression
(e1,e2)->e1+e2

```

### Syntax

a lambda consists of a **parameter list** followed by theh **arrow token** (->) and a **body**
(parameter list) -> {statements}

Method:

```java
int min(int num1,int num2){
    if(num1<num2){
        return num1;
    }
    return num2;
}
```

Lambda:

```java
(int num1,int num2)->{
    if(num1<num2){
        return num1;
    }
    return num2;
}
```

### Implicit Parameter Types

The **parameter types** are usually **omitted**

Method:

```java
int min(int num1, int num2) {
    if (num1 < num2) {
        return num1;
    }
    return num2;
}

```

Lambda:

```java
(num1, num2) -> {
    if (num1 < num2) {
        return num1;
    }
    return num2;
}
```

The **compiler** can **determine** the **parameter types** by the **lambda’s context**

### Implicit Return

When the **body** contains only **one expression**, the return keyword and curly braces **{}** may also be **omitted**

Method:

```java
int sum(int num1, int num2)
{
    return num1 + num2;
}
```

Lambda:

```java
(num1, num2) -> {
return num1 + num2;
}

//......

(num1, num2) -> num1 + num2

```

The **compiler** can also **determine** the **return type** by the lambda’s context

**Question**
What is/are the data type(s) of variables n1, n2, and n1 + n2?

```java
public static void sum() {
    double[] arr = { 1.1, 2.2, 3.3 };
    double sum =
        DoubleStream.of(arr)
            .reduce((n1, n2) -> n1 + n2))
            .getAsDouble();
    System.out.println("Sum: " + sum);
}

```

### One Parameter

When the **parameter list** contains only **one parameter**, **parameter parentheses** may also be **omitted**

```java
//Method:
void printValue(
        double myValue) {
    System.out.println(myValue);
}

//Lambda:
(myValue) -> 
    System.out.println(myValue)

//Lambda
myValue -> 
    System.out.println(myValue)

```

In the previous slide, how can the compiler know the method’s **return type** is integer while it is void in this slide?

### Method Reference

When a lambda expression does **nothing** but **calls one existing method**, we can just refer to **ClassName::methodName**

```java
//Method:
void printValue(double myValue) {
    System.out.println(myValue);
}

//Lambda:
myValue -> 
    System.out.println(myValue)

//Method Reference:
System.out::println

```

```java
//Method:
int compare(Person a, Person b) {
    return a.compareByAge(b);
}

//Lambda
(a, b) -> a.compareByAge(b)

//Method Reference:
Person::compareByAge

```

### Lambdas Summary

1. A lambda is a method with **no method name, no return type**
2. A nd remove **types** of **parameters**
3. **One expression** in method body? Remove **return** keyword and **curly braces {}**
4. Only **one parameter**? Remove **parameter parentheses ()**

### Quiz

Convert each of the following methods to lambdas or method references, in its **shortest form**

```java
int pow(int base, int exp) {
    int res = 1;
    for (int i = 0; i < exp; i++) {
        res *= base;
    }
return res;
}


//Answer:
(base, exp) -> {
    int res = 1;
    for (int i = 0; i < exp; i++) {
        res *= base;
    }
return res;
}


```

Convert each of the following methods to lambdas or method references, in its **shortest form**

```java
boolean isPrime(int num) {
    for (int divisor = 2; 
        divisor < num - 1; divisor++) {
        if (num % divisor == 0)
        return false;
    }
return true;
}

//Answer:

num -> {
    for (int divisor = 2; divisor<num-1; divisor++) {
        if (num % divisor == 0)
        return false;
    }
return true;
}
```

### More Practise

Convert the following methods to lambda expressions or method references, in its **shortest form**

```java
String getFullName(Person p) {
    return p.getFirstName() + " " + p.getLastName();
}

//Answer:
p -> p.getFirstName() + " " + p.getLastName()

```

```java
void printFullName(Person p) {
    System.out.println(p.getFirstName() + " " + p.getLastName());
}

//Answer:
p -> System.out.println(p.getFirstName() + " "+ p.getLastName())

```

```java
String getFirstName(Person p) {
    return p.getFirstName();
}

class Person {
    private String firstName;
    private String lastName;
    // Other code omitted 
    // for brevity
}

//Answer:
Person::getFirstName

```

## Function Interface

- A functional interface contains exactly **one abstract** method, called functional method
- Compiler can **map** a **lambda** to some respective **functional interfaces**

```java
interface Consumer<T> {
    void accept(T t);
}

interface Predicate<T> {
    boolean test(T t);
}

interface BinaryOperator<T> {
    T apply(T t1, T t2);
}

```

### An example - Consumer

Interface Consumer<T>, method accept(T) **Perform** a**task** with the given T, e.g.,

```java
interface Consumer<T> {
    void accept(T t);
}

```

Like other interfaces, we need to **implement** the
**abstract method** when implementing a functional
interface

How to implement and instantiate instances of functional interfaces?

### Instance Instantiation

We can **implement** and **instantiate** an instance of a functional interface by new keyword and implement its abstract methods

```java
new Consumer< Integer>() {
    @Override
    public void accept(Integer num) {
        System.out.println(num);
    }
}

```

1. To instantiate an instance, start with new and the interface name **Consumer**
2. Because interface **Consumer<T>** supports generic type, specify the generic type. In here, it is **Integer**
3. Implement the interface’s abstract method. In here, **accept(T)** becomes **accept(Integer)**

### Functional Interfaces and Lambdas

We can also **implement** and **instantiate** an instance of a functional interface with the **respective lambdas**

```java
new Consumer<Integer>() {
    @Override
    public void accept(Integer num) {
        System.out.println(num);
    }
}

//lambda
num -> System.out.println(num)

//Lambda
System.out::println
```

Both lambdas can be used as instances of Consumer

Conversely, given the above lambdas, can the compiler know how to map to Consumer?
Hint: a functional interface only has 1 functional method

### Using Functional Interfaces

Functional interfaces are usually used **as method parameters** For example,later we’ll study streams, which can be iterated using
forEach(Consumer<T>) method

```java
Integer[] arr = {1, 2, 3, 4};
    Arrays.stream(arr)
        .forEach( new Consumer<Integer>() {
            @Override
            public void accept(Integer num) {
                System.out.println(num);
            }
        });


Integer[] arr = {1, 2, 3, 4};
Arrays.stream(arr)
    .forEach( num -> 
        System.out.println(num)); 
```

In both cases, an instance of Consumer is used as the method **parameters**

#### A Example -Pridicate

Interface Predicate<T>, method test(T) Test whether the T argument satisfy a condition

```java
interface Predicate<T> {
    boolean test(T t);
}

```

Like Consumer<T>, we can **implement** and **instantiate** an instance of Predicate<T> as follows

```java
new Predicate<Person>() {
    @Override
    public boolean test(Person p) {
        return p.getKids() == 2; 
    }
}

//Lambda


```

#### An example – BinaryOperator

Interface BinaryOperator<T>, method apply(T, T)Performs an operation on the 2 arguments (such as a  calculation) and returns a value of the same type

```java
interface BinaryOperator<T> {
    T apply(T t1, T t2);
}

```

Some lambdas that may be used as instances of
BinaryOperator

```java
(x, y) -> x + y

(str1, str2) -> 
str1 + " " + str2

(x, y) -> {
    if (x > y)
        return x - y;
    return y - x;
};

```

#### Common Functional Interfaces

|interface|Method|Arguments|What does it do|Return|
|:---|:---|:---|:---|:---|
|Consumer<T>| accept| T |Perform a task with T, e.g., printing| void|
|Function<T,R>| apply| T| Call a method on the T argument and return that method’s result|R|
|Predicate<T>| test| T |Test whether the T argument satisfies a condition|bool|
|Supplier<T>| get| Empty| Produce a value of type T, e.g., creating a collection object|T|
|UnaryOperator<T>| apply| T| Perform an operation on the T argument and return a value of T|T|
|BinaryOperator<T>| apply| T, T| Perform an operation on the two| T| arguments and return a value of type T||T|

## Streams

### Revisit - working with Collections

When processing a collection, we usually

1. Iterate over its elements
2. Do some work with each element

```java
public static int
    sumOfEven(int[] arr) {
        int sum = 0;
        for (int num: arr) {
            if (num % 2 == 0) {
                sum += num;
        }
    }
    return sum;
}
```

### What are Streams?

- A Stream is a sequence of elements on which  we perform tasks
- Specify only what we want to do
- Then simply let the Stream deal with how to do it

```java
public static int
    sumOfEven2(int[] arr) {
        return IntStream
    .of(arr)
    .filter(x -> x%2==0)
    .sum();
}
```

### Stream Pipelines

Source=>Create Stream=>Operation 1=>Operation2=>.....Operation N=>Terminal Operation=>Result

Source: Usually , an array or a collection
Operation: Filtering, sorting,type conversions, mapping...
Terminal operation: Aggregate results, eg., count, sum or collecting a collection.

```java
IntStream.of(arr).filter(x -> x % 2 ==0).sum()

//arr: Source
//of: Create stream
//filter: Intermediate operation
//sum: Terminal operation

```

### Lazy vs eager operations

**Intermediate** operations are **lazy**

=> *Not perform until a
terminal operation is
called*

**Terminal** operations are **eager**
=> Perform **right away** when being called

### Stream Advantages

- Allows us to write more declarative and more concise programs
- Allows us to focus on the problem rather than the code
- Facilitates parallelism

### Creating Streams from Arrays

Streams can be created from arrays with different
approaches

```java
public static void streamFromArray1() {
    int[] arr = {3, 10, 6, 1, 4}; 
        IntStream.of(arr)
            .forEach(e -> System.out.print(e + " "));
}

//3 10 6 1 4

public static void streamFromArray2() {
    Integer[] arr = {2, 9, 5, 0, 3};
        Arrays.stream(arr)
            .forEach(e -> System.out.print(e + " "));
}
//2 9 5 0 3

```

### Creating Streams from Collections

Streams can also be created from any implementation of Collection interface, e.g., List, Set…

```java
public static void streamFromList() {
    List<String> myList = new ArrayList<>();
    myList.add("Hi");
    myList.add("SA");
    myList.add("students");
    myList.stream()
        .forEach(System.out::println);
}

//Hi
//SA
//students
```

### Creating ordered sequence of integer Streams

Ordered sequence of integers can be created using
IntStream.range() and IntStream.rangeClosed()

```java
public static void orderedSequenceStream1() {
    IntStream
        .range(1, 10)
        .forEach(e -> System.out.print(e + " "));
}
//1 2 3 4 5 6 7 8 9

public static void orderedSequenceStream2() {
    IntStream
        .rangeClosed(1, 10)
        .forEach(e -> System.out.print(e + " "));
}

//1 2 3 4 5 6 7 8 9 10

```

### Common Intermediate Operations

|Method| Parameter| Description|
|-----|-----|-----|
|filter| Predicate<T>| Returns a stream consisting of the elements of this stream that match the given predicate.|
|sorted| Comparator<T>| Returns a stream consisting of the elements of this stream, sorted according to the provided Comparator.|
|map| Function<T, R>| Returns a stream consisting of the results of applying the given function to the elements of this stream.|
|distinct| No| Returns a stream consisting of the distinct elements (according to Object.equals(Object)) of this stream.|
|limit| long| Returns a stream consisting of the elements of this stream, truncated to be no longer than the given number in length.|
|skip| long| Returns a stream consisting of the remaining elements of this stream after discarding the first n elements of the stream. If this stream contains fewer than n elements then an empty stream will be returned|

### Filtering

Elements in a stream can be filtered using filter(Predicate<T>)

```java
public static List<Person> generatePersonList() 
{
    List<Person> l = new ArrayList<>();
    l.add(new Person("John", "Tan", 32, 2));
    l.add(new Person("Jessica", "Lim", 28, 3));
    l.add(new Person("Mary", "Lee", 42, 2));
    l.add(new Person("Jason", "Ng", 33, 1));
    l.add(new Person("Mike", "Ong", 22, 0));
    return l;
}

public static void filtering() {
    List<Person> persons = 
    generatePersonList();
    persons
    .stream()
    .filter( x -> x.getKids() == 2)
    .forEach(System.out::println);
}


class Person {
    private String 
    firstName;
    private String 
    lastName;
    private int age;
    private int kids;
    // Other code omitted 
    // for brevity
}


//John, Tan, 32, 2
//Mary, Lee, 42, 2
```

### One way to understand a stream’s method

```java
.filter(x -> x.getKids() == 2)
```

1. I want to filter elements in a stream
2. Ok, you need to call filter() method, and give me a Predicate in form of a lambda
3. I will loop through every element in the stream, and with the current element…
4. Let’s name that element as x, the left side of the lambda
5. You need to let me know what to do with x, using any method of the data type in the stream holding x. For example, Person in the last slide
6. I want to filter only x
having 2 kids, so I return a Boolean Expression with such condition. It is put in the body, the right side of the lambda

=>Most of stream methods happen in this manner. Step 2, 4 and 6 change depending on the scenario

### Sorting

Streams can be sorted using sorted(Comparator<T>) As usual, a Comparator<T> object can be created using a lambda

```java
public static List<Person> generatePersonList() {
    List<Person> l = new ArrayList<>();
    l.add(new Person("John", "Tan", 32, 2));
    l.add(new Person("Jessica", "Lim", 28, 3));
    l.add(new Person("Mary", "Lee", 42, 2));
    l.add(new Person("Jason", "Ng", 33, 1));
    l.add(new Person("Mike", "Ong", 22, 0));
    return l;
}

public static void sortBySingleField() {
    List<Person> persons = generatePersonList();
    persons
    .stream()
    .sorted( (p1, p2) -> 
    p1.getFirstName().compareTo( 
    p2.getFirstName()))
    .forEach(x -> System.out.println(x));
}


//Jason, Ng, 33, 1
//Jessica, Lim, 28, 3
//John, Tan, 32, 2
//Mary, Lee, 42, 2
//Mike, Ong, 22, 0

```

Alternatively, a Comparator<T> object can be created using Comparator.comparing(Function<T,R>)

```java
public static List<Person> generatePersonList() {
    List<Person> l = new ArrayList<>();
    l.add(new Person("John", "Tan", 32, 2));
    l.add(new Person("Jessica", "Lim", 28, 3));
    l.add(new Person("Mary", "Lee", 42, 2));
    l.add(new Person("Jason", "Ng", 33, 1));
    l.add(new Person("Mike", "Ong", 22, 0));
    return l;
}

public static void sortBySingleField () {
    List<Person> persons = generatePersonList();
    persons
    .stream()
    .sorted( Comparator.comparing(
    Person::getFirstName))
    .forEach(x -> System.out.println(x));
}

//Jason, Ng, 33, 1
//Jessica, Lim, 28, 3
//John, Tan, 32, 2
//Mary, Lee, 42, 2
//Mike, Ong, 22, 0

```

Streams can also be sorted with multiple fields and in reversed order

```java
public static List<Person> generatePersonList() {
    List<Person> l = new ArrayList<>();
    l.add(new Person("John", "Tan", 32, 2));
    l.add(new Person("Jessica", "Lim", 28, 3));
    l.add(new Person("Mary", "Lee", 42, 2));
    l.add(new Person("Jason", "Ng", 33, 1));
    l.add(new Person("Mike", "Ong", 22, 0));
    return l;
}

public static void sortByMultiFields () {
    List<Person> persons = generatePersonList();
    persons.stream()
    .sorted(Comparator
    .comparing(Person::getKids)
    .thenComparing(Person::getAge)
    .reversed())
    .forEach(x -> System.out.println(x));
}

//Jason, Ng, 33, 1
//Jessica, Lim, 28, 3
//John, Tan, 32, 2
//Mary, Lee, 42, 2
//Mike, Ong, 22, 0

```

### Transforming

Each of elements in a Stream can be transformed to another value (even another type) using map(Function<T,R>)

```java
public static List<Person> generatePersonList() {
    List<Person> l = new ArrayList<>();
    l.add(new Person("John", "Tan", 32, 2));
    l.add(new Person("Jessica", "Lim", 28, 3));
    l.add(new Person("Mary", "Lee", 42, 2));
    l.add(new Person("Jason", "Ng", 33, 1));
    l.add(new Person("Mike", "Ong", 22, 0));
    return l;
}

public static void transforming1() {
    List<Person> persons = generatePersonList();
    persons
    .stream()
    .sorted(Comparator.comparing(
    Person::getFirstName))
    .map( x -> x.getFirstName() + 
    " " + x.getLastName())
    .forEach(System.out::println);
}


//Jason Ng
//Jessica Lim
//John Tan
//Mary Lee
//Mike Ong

```

Of course, map(Function<T,R>) can also be applied to
other types of streams

```java
public static void transforming2() {
    int[] arr = {0, 1, 2, 3, 4, 5};
    IntStream.of(arr)
    .map(e -> e * 2)
    .forEach(e -> System.out.print(e + " "));
}

//0 2 4 6 8 10

public static void transforming3() {
    String[] arr = {"aa", "bb", "cc", "dd"};
    Arrays.stream(arr)
    .map(String::toUpperCase)
    .forEach(e -> System.out.print(e + " "));
}
//AA BB CC DC

```

A stream can be mapped to a numeric stream

```java
public static void transforming4() {
String[] names = 
{"John", "Jessica", "Mary", "Jason", "Mike"};
int maxLength =
Stream.of(names)
.mapToInt(x -> x.length())
.max()
.getAsInt();
System.out.println("Name with maximum length is " + 
maxLength);

//Name with maximum length is 7

```

### Terminal Operations

|Method| Parameters| Description|
|---|---|---|
|forEach| Consumer<T>| Performs an action for each element of this stream.|
|reduce| T, BinaryOperator<T>| Performs a reduction on the elements of this stream, using the provided identity value and an associative accumulation function, and returns the reduced value.|
|reduce| BinaryOperator<T>| Performs a reduction on the elements of this stream, using an associative accumulation function, and returns an Optional describing the reduced value, if any.|
|min| Comparator<T>| Returns the minimum element of this stream according to the provided Comparator. This is a special case of a reduction.|
|max| Comparator<T>| Returns the maximum element of this stream according to the provided Comparator. This is a special case of a reduction.|
|average| No Return| the average of all elements in a numeric stream.|

### “Iterating"

Performs an action for each element of the stream using forEach(Consumer<T>)

```java
public static void forEachStream() 
{
    List<String> list = new ArrayList<>();
    list.add("aa");
    list.add("bb");
    list.add("cc");
    list
    .stream()
    .forEach( e
    -> System.out.print(e + " "));
}

//aa bb cc
```

 1. Call forEach()
 2. Given a Consumer object in the form of lambda as the  argument We can think like this: given each element e, what Java should do with it (and return  nothing as defined in Consumer interface)? In here,we ask Java to print the value of element e

=>List also has forEach() method, operating in the
same manner