o7planning

Java BiFunction Tutorial with Examples

  1. BiFunction
  2. andThen(Function after)
  3. BiFunction + Method reference
  4. BiFunction + Constructor reference

1. BiFunction

In Java 8, BiFunction is a functional interface which represents an operator that accepts two input values and returns one value.
Source code of BiFunction interface:
BiFunction
@FunctionalInterface
public interface BiFunction<T, U, R> {
    R apply(T t, U u);
 
    default <V> BiFunction<T, U, V> andThen(Function<? super R, ? extends V> after) {
        Objects.requireNonNull(after);
        return (T t, U u) -> after.apply(apply(t, u));
    }
}
See also: Function is a functional interrface similar to BiFunction which accepts an input parameter and returns a value.
Example: Create a BiFunction that accepts 2 input parameters of type String and returns a String.
BiFunctionEx1.java
package org.o7planning.bifunction.ex;

import java.util.function.BiFunction;

public class BiFunctionEx1 {
    
    public static void main(String[] args) {
        
        // Create a BiFunction object directly.
        // Accepts 2 input parameters of type String and return a String.
        BiFunction<String, String, String> namer //
                = (firstName, lastName) -> firstName + " " + lastName;

        String fullName = namer.apply("James", "Smith");

        System.out.println(fullName);
    }
}
Output:
James Smith
Below is a list of methods in java.util package using BiFunction:
V
HashMap.compute(K key, BiFunction<? super K,? super V,? extends V> remappingFunction)
V
Hashtable.compute(K key, BiFunction<? super K,? super V,? extends V> remappingFunction)
default V
Map.compute(K key, BiFunction<? super K,? super V,? extends V> remappingFunction)
V
TreeMap.compute(K key, BiFunction<? super K,? super V,? extends V> remappingFunction)
V
HashMap.computeIfPresent(K key, BiFunction<? super K,? super V,? extends V> remappingFunction)
V
Hashtable.computeIfPresent(K key, BiFunction<? super K,? super V,? extends V> remappingFunction)
default V
Map.computeIfPresent(K key, BiFunction<? super K,? super V,? extends V> remappingFunction)
V
TreeMap.computeIfPresent(K key, BiFunction<? super K,? super V,? extends V> remappingFunction)
V
HashMap.merge(K key, V value, BiFunction<? super V,? super V,? extends V> remappingFunction)
V
Hashtable.merge(K key, V value, BiFunction<? super V,? super V,? extends V> remappingFunction)
default V
Map.merge(K key, V value, BiFunction<? super V,? super V,? extends V> remappingFunction)
V
TreeMap.merge(K key, V value, BiFunction<? super V,? super V,? extends V> remappingFunction)
default void
Map.replaceAll(BiFunction<? super K,? super V,? extends V> function)
Example: A Map object contains mappings between country name and apartment renting cost in a month. We update 10% increase except Vietnam.
BiFunctionEx2.java
package org.o7planning.bifunction.ex;

import java.util.HashMap;
import java.util.Map;
import java.util.function.BiFunction;

public class BiFunctionEx2 {

    public static void main(String[] args) {
        // Data in 2021.
        // String country --> Float price to rent an apartment per month ($).
        Map<String, Float> pricingMap = new HashMap<String, Float>();

        pricingMap.put("Singapore", 2147f);
        pricingMap.put("Sweden", 890f);
        pricingMap.put("Japan", 770f);
        pricingMap.put("Mexico", 345f);
        pricingMap.put("Vietnam", 305f);
        pricingMap.put("India", 156f);
        
        // Pricing Map
        System.out.println(pricingMap);
        System.out.println();

        // (String,Float) --> Float.
        BiFunction<String, Float, Float> biFunction = (country, price) -> {
            if (country.equals("Vietnam")) {
                return price;
            }
            return price * 1.1f;
        };
        
        pricingMap.replaceAll(biFunction);
        
        // After replacement.
        System.out.println("After replacement:\n");
        System.out.println(pricingMap);
    }
}
Output:
{Sweden=890.0, Vietnam=305.0, Singapore=2147.0, Japan=770.0, Mexico=345.0, India=156.0}

After replacement:

{Sweden=979.0, Vietnam=305.0, Singapore=2361.7, Japan=847.0, Mexico=379.5, India=171.6}

2. andThen(Function after)

andThen(after) method returns a combined BiFunction. First, BiFunction.apply is called to turn (T,U) into (R). Then after.apply is called to turn (R) into (V). If an error occurs in one of the above 2 steps, the error is passed to the caller. If an error occurs at the current BiFunction then after is ignored.
Source code of andThen(after) method can be a bit confusing:
@FunctionalInterface
public interface BiFunction<T, U, R> {
    R apply(T t, U u);

    default <V> BiFunction<T, U, V> andThen(Function<? super R, ? extends V> after) {
        Objects.requireNonNull(after);
        return (T t, U u) -> after.apply(apply(t, u));
    }
}
And we can interpret the above source code in a simpler way:
BiFunction (Simpler)
@FunctionalInterface
public interface BiFunction<T, U, R> {
    // (T,U) -> R
    R apply(T t, U u);

    // (T,U) -> V
    default <V> BiFunction<T, U, V> andThen(Function<? super R, ? extends V> after) {
        Objects.requireNonNull(after);
        
        BiFunction<T, U, V> ret = (T t, U u) -> {
            // (T,U) -> R
            R r = this.apply(t, u);    
            
            // R -> V
            V v = after.apply(r);
            return v;
        };
        
        return ret;
    }
}
Example:
BiFunction_andThen_ex1.java
package org.o7planning.bifunction.ex;

import java.util.function.Function;

public class BiFunction_andThen_ex1 {

    public static void main(String[] args) {
        // BiFunction: (char,int) --> (String)
        // Example: ('0', 3)  --> "000"
        BiFunction<Character, Integer, String> bif = (ch, n) -> {
            String s="";
            for(int i=0;i< n;i++) {
                s+=ch;
            }
            return s;
        };
        // Function: (String) --> (String)
        // Example: "abc" --> "ABC".
        Function<String,String> after = s -> s.toUpperCase();
        
        // Test:
        String result = bif.andThen(after).apply('a', 3);
        System.out.println("Result: " + result);
    }
}
Output:
Result: AAA

3. BiFunction + Method reference

If a method takes two parameters and returns a value then its reference can be considered as a BiFunction.
Example: Math.max(int,int) static method accepts 2 parameters and returns an Integer. So its reference is Math::max can be considered a BiFunction.
BiFunction_mRef_ex1.java
package org.o7planning.bifunction.ex;

public class BiFunction_mRef_ex1 {

    public static void main(String[] args) {
        // Create a BiFunction from method reference.
        // (Integer,Integer) --> (Integer)
        BiFunction<Integer, Integer, Integer> biFunc = Math::max;

        int max = biFunc.apply(10,  20); // 20
        
        System.out.println("Max: " + max); // 20
    }
}
Output:
Max: 20
Example: BiFunction + non-static method reference:
BiFunction_mRef_ex2.java
package org.o7planning.bifunction.ex;

public class BiFunction_mRef_ex2 {

    public static void main(String[] args) {
        CardTemplate template = new CardTemplate("Designed by Tom");

        // Create a BiFunction from method reference.
        // (String,String) --> (String)
        BiFunction<String, String, String> biFunc = template::getContent;

        String cardContent = biFunc.apply("Eli", "Smith");

        System.out.println(cardContent);
    }
}

class CardTemplate {
    private String someInfo;

    public CardTemplate(String someInfo) {
        this.someInfo = someInfo;
    }

    public String getContent(String firstName, String lastName) {
        return "----- ~~~~~ -----\n" //
                + "First name: " + firstName + "\n" //
                + "Last name: " + lastName + "\n" //
                + this.someInfo;
    }
}
Output:
----- ~~~~~ -----
First name: Eli
Last name: Smith
Designed by Tom

4. BiFunction + Constructor reference

As we all know, a constructor is a special method that returns an object. So, if a constructor has 2 parameters, its reference will be considered as a BiFunction.
BiFunction_cRef_ex1.java
package org.o7planning.bifunction.ex;

public class BiFunction_cRef_ex1 {

    public static void main(String[] args) {
        // Create a BiFunction from constructor reference.
        // (String,Integer) --> (Staff)
        BiFunction<String, Integer, Staff> biFunc = Staff::new;

        Staff tom = biFunc.apply("Tom", 1000);
        Staff jerry = biFunc.apply("Jerry", 2000);
        
        tom.showInfo();
        jerry.showInfo();
    }
    
    public static class Staff {
        private String fullName;
        private int salary;

        public Staff(String fullName,int salary)  {
            this.fullName= fullName;
            this.salary = salary;
        }
        public String getFullName() {
            return fullName;
        }
        public int getSalary() {
            return salary;
        }
        public void showInfo()  {
            System.out.println("Full Name: " + this.fullName +", Salary: " + this.salary);
        }
    }
}
Output:
Full Name: Tom, Salary: 1000
Full Name: Jerry, Salary: 2000

Java Basic

Show More