A Guide to Java BinaryOperator Interface

Leverage Java BinaryOperator interface for functional programming by using lambda expressions, method references, and type-specific versions like IntBinaryOperator.
On this page

A Guide to Java BinaryOperator Interface

The BinaryOperator interface in Java is a powerful tool for functional programming. As a functional interface, BinaryOperator represents an operation upon two operands of the same type, producing a result of that same type. In this article, we will explore how Java’s BinaryOperator can be leveraged for cleaner and more declarative code.

The BinaryOperator interface was introduced in Java 8 along with lambda expressions and stream APIs. It resides in the java.util.function package and takes two generic arguments of the same type T. BinaryOperator extends the BiFunction interface but constrains the parameter types and return type to all be identical. This makes BinaryOperator well-suited for use cases like mathematical operations, reductions, and finding min/max based on comparators.

Some key methods provided by the BinaryOperator interface include:

  • apply() - Applies the lambda operation on two operands
  • minBy() - Returns a minimum value based on a comparator
  • maxBy() - Returns a maximum value based on a comparator

By using Java’s BinaryOperator functional interface, developers can write concise yet declarative functional code. In the rest of this article, we will explore BinaryOperator usage in more depth with code examples.

Introduction to BinaryOperator

The BinaryOperator interface belongs to the java.util.function package. It takes two generic arguments of type T, operates on them, and returns a result of type T.

For example:

1BinaryOperator<Integer> add = (x, y) -> x + y;
2
3Integer result = add.apply(2, 3); //result is 5

The key features of BinaryOperator include:

Compared to BiFunction which can take different argument types, BinaryOperator constraints the types to be identical. This makes it convenient for mathematical and reduction operations.

BinaryOperator Methods

The BinaryOperator interface contains three main methods:

apply()

This abstract method accepts two parameters of type T and produces a result of type T. Lambda expressions or method references implement the apply() logic.

For example:

1BinaryOperator<String> concat = (str1, str2) -> str1 + str2;
2
3String result = concat.apply("Hello", "IToolkit"); //"HelloIToolkit"

minBy()

This static method accepts a Comparator and returns a BinaryOperator that returns the minimum of two values as defined by the Comparator.

For example:

1Comparator<Integer> cmp = (a, b) -> a.compareTo(b);
2
3BinaryOperator<Integer> min = BinaryOperator.minBy(cmp);
4
5Integer minVal = min.apply(5, 3); // 3

maxBy()

Similar to minBy(), but returns a BinaryOperator that returns the maximum of two values as defined by the passed Comparator.

For example:

1Comparator<String> cmp = (a, b) -> b.length() - a.length();
2
3BinaryOperator<String> max = BinaryOperator.maxBy(cmp);
4
5String maxStr = max.apply("Apple", "Banana"); // "Banana"

Using BinaryOperators

There are a few common ways BinaryOperators can be used:

With Lambda Expressions

1BinaryOperator<Long> addLongs = (x, y) -> x + y;
2
3long sum = addLongs.apply(5L, 10L); //15

With Method References

1BinaryOperator<String> concat = String::concat;
2
3String result = concat.apply("Hello ", "IToolkit!"); //"Hello IToolkit!"

Passing As Parameters

Can be passed to higher-order functions:

1public static <T> T reduce(List<T> list, T identity, BinaryOperator<T> accumulator){
2  T result = identity;
3  for(T t : list){
4     result = accumulator.apply(result, t);
5  }
6  return result;
7}
8
9Integer sum = reduce(Arrays.asList(1, 2, 3), 0, Integer::sum);

Type-Specific BinaryOperators

For better performance with primitive types, specialized BinaryOperator versions exist:

  • IntBinaryOperator
  • LongBinaryOperator
  • DoubleBinaryOperator

These should be used instead of boxed BinaryOperators when operating on primitive types.

For example:

1IntBinaryOperator add = (x, y) -> x + y;
2int sum = add.applyAsInt(3, 5); // 8

BinaryOperator Code Examples

Here are some examples demonstrating common use cases:

Basic Usage

1// String concatenation
2BinaryOperator<String> concat = String::concat;
3String result = concat.apply("Hello ", "IToolkit!");
4
5// Numeric addition
6BinaryOperator<Integer> add = (x, y) -> x + y;
7int sum = add.apply(3, 5);

Finding Min/Max

1// Find min length string
2Comparator<String> cmp = Comparator.comparingInt(String::length);
3BinaryOperator<String> min = BinaryOperator.minBy(cmp);
4String minStr = min.apply("Apple", "Banana");
5
6// Find max length string
7BinaryOperator<String> max = BinaryOperator.maxBy(cmp);
8String maxStr = max.apply("Apple", "Banana");

Reducing Streams

1Integer sum = list.stream()
2  .reduce(0, (a, b) -> a + b);
3
4// With method reference
5Integer sum = list.stream()
6  .reduce(0, Integer::sum);

Primitive Types

1IntBinaryOperator add = (x, y) -> x + y;
2int sum = add.applyAsInt(3, 5);
3
4LongBinaryOperator longAdd = (x, y) -> x + y;
5long longSum = longAdd.applyAsLong(10000, 20000);

When to Use BinaryOperator

BinaryOperator is useful in these cases:

  • Reducing parallel streams - concurrency safe
  • Mathematical operations on the same types
  • Finding min/max based on custom comparators
  • Any generic functional operation on two same-typed operands

It provides a simple functional interface for these symmetric binary operations.

Conclusion

The BinaryOperator interface is a handy functional interface for operating on two values of the same type. It reduces boilerplate code over anonymous inner classes.

Common use cases include reducing collections, mathematical operations, finding min/max values based on custom comparisons, and eliminating null checks.

By leveraging BinaryOperator in Java 8 code alongside streams and lambdas, you can write very concise yet readable functional-style code.