Java BiFunction Tutorial

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:

Running BiFunction

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:

Applying a BiFunction on different object types

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:

  1. First, calculate the total (without shipping and tax)
  2. Next, calculate the total with the shipping
  3. 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:

Chaining BiFunction with Function

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.

Leave a Comment