Table of Contents
Overview
In Java, A BiFunction represents a function that accepts two arguments and returns a result. The interface has one abstract method apply
and one default method andThen
.
The apply
method is defined as follows:
R apply(T t, U u);
That means it accepts two arguments (t of type T and u of type U) and returns an object of type R.
After reading this post, you will (hopefully) know the following details:
- Basic usage of BiFunction
- How to use BiFunction in your code
- Some practical use cases
Basic Usage
Let’s consider some common operations with BiFunction.
Creating and calling BiFunction
public static void main(String[] args) { //create a BiFunction BiFunction<String, String, String> stringJoiner = (String a, String b) -> a + b; //call the apply method System.out.println(stringJoiner.apply("Hello ", "World")); }
The code above creates a BiFunction called stringJoiner
. As you can see, the types of the arguments and the return object don’t need to be different.
Running the code above would provide the following results:
Let’s try something less boring than just String. As you may know, a male Donkey and a female Horse went to a bar and sometime later, they will produce a Mule. However, if a female Donkey and a male Horse went to a bar, the couple would produce a Hinny!
Let’s first create some classes to represent the animals:
enum Gender { MALE, FEMALE } record Horse(String name, Gender gender) { } record Donkey(String name, Gender gender) { } interface HorseDonkey { } record Mule(String name) implements HorseDonkey { @Override public String toString() { return "Mule{" + "name='" + name + '\'' + '}'; } } record Hinny(String name) implements HorseDonkey { @Override public String toString() { return "Hinny{" + "name='" + name + '\'' + '}'; } }
And here is the BiFunction
BiFunction<Horse, Donkey, HorseDonkey> downtownBar = (Horse horse, Donkey donkey) -> { if (horse.gender() == Gender.FEMALE && donkey.gender() == Gender.MALE) { return new Mule(horse.name() + "\uD83D\uDD25" + donkey.name()); } else if (horse.gender() == Gender.MALE && donkey.gender() == Gender.FEMALE) { return new Hinny(horse.name() + "\uD83D\uDD25" + donkey.name()); } throw new RuntimeException("Not implemented yet"); };
Let’s try with Jack the male horse and Jane the female Donkey.
public static void main(String[] args) { BiFunction<Horse, Donkey, HorseDonkey> downtownBar = (Horse horse, Donkey donkey) -> { if (horse.gender() == Gender.FEMALE && donkey.gender() == Gender.MALE) { return new Mule(horse.name() + "\uD83D\uDD25" + donkey.name()); } else if (horse.gender() == Gender.MALE && donkey.gender() == Gender.FEMALE) { return new Hinny(horse.name() + "\uD83D\uDD25" + donkey.name()); } throw new RuntimeException("Not implemented yet"); }; var jack = new Horse("Jack", Gender.MALE); var jane = new Donkey("Jane", Gender.FEMALE); var horseDonkey = downtownBar.apply(jack, jane); System.out.println(horseDonkey); }
You can expect that the result is a Hinny:
Chaining BiFunction and Function
Imagine you need to write a function to calculate the shipping fee and tax for an order of many items. The steps are:
- First, calculate the total (without shipping and tax)
- Next, calculate the total with the shipping
- Finally, calculate the tax based on the total with shipping
The BiFunction interface has a default method called andThen
that accepts a Function. You can achieve the above requirements with the following code:
record CartLine(String name, int quantity, double price) { } public static void main(String[] args) { var cart = List.of( new CartLine("Apple", 2, 1.5), new CartLine("Orange", 3, 2.0), new CartLine("Banana", 1, 3.0) ); var shippingRate = 0.5; var tax = 0.1; BiFunction<Double, Double, Double> shippingBifunction = (total, rate) -> total * (1 + rate); Function<Double, Double> taxFunction = (total) -> total * (1 + tax); var cartTotal = cart.stream() .mapToDouble(line -> line.price() * line.quantity()) .sum(); var finalTotal = shippingBifunction.andThen(taxFunction); System.out.println(finalTotal.apply(cartTotal, shippingRate)); }
The andThen
method returns a BiFunction. That means you can keep calling andThen
to pass in as many Functions as you need.
Running the code would provide the following result:
Conclusion
In this post, I’ve shown you the basic usages of the BiFunction functional interface in Java. To use this interface, you need to provide the implementation for the apply method. You can also use the andThen
method to chain subsequent functions to further process the result.
I build softwares that solve problems. I also love writing/documenting things I learn/want to learn.