Why do .maxIntegermax and .minIntegermin compile on a Java 8 Stream?
Interview Questions practice on Codemia
Over 8,000 real interview questions from top companies, searchable by company and role.
Introduction
In Java 8, the Streams API was introduced to provide a functional approach to processing sequences of elements. One of the key features of Streams is the ability to aggregate data using reduction operations. In this article, we will explore how and why the methods .max(Integer::max) and .min(Integer::min) successfully compile and operate on Java Streams, diving into the technical explanations of method references and functional interfaces.
Concepts and Components
Before diving into the specifics of .max(Integer::max) and .min(Integer::min), it is essential to understand some foundational Java concepts:
- Streams: Streams represent a sequence of elements and support different kinds of operations to perform computations with those elements.
- Reduction Operations: These operations are used to combine the elements of a stream into a single result, such as finding the sum, the maximum, or the minimum.
- Method References: Introduced in Java 8, method references provide a way to refer to methods without invoking them. They are used to create lambda expressions and can be used with static methods, instance methods, and constructors.
- Functional Interfaces: A functional interface is an interface that contains a single abstract method. In the context of streams, functional interfaces define the operations to be performed in a stream.
How .max(Integer::max) and .min(Integer::min) Work
Method Reference
The expressions Integer::max and Integer::min are examples of static method references. They point to the static methods max and min defined in the Integer class.
Reduction Using max and min
The Stream class provides max and min methods that facilitate reduction operations:
Optional<T> max(Comparator<? super T> comparator)Optional<T> min(Comparator<? super T> comparator)
These methods require a Comparator to determine the maximum or minimum element of the stream. However, Integer::max and Integer::min are often mistakenly assumed to directly correspond to these methods. Instead, they are typically used with the reduce method.
Example with reduce
The reduce method can take an associative function to reduce the elements of a stream.
For example:
Here, Integer::max and Integer::min serve as BinaryOperators which are functional interfaces. They take two inputs of the same type and return a result of that type.
Why They Compile
- BinaryOperator Functional Interface: Both
Integer::maxandInteger::minare compatible withBinaryOperator<Integer>, a specialization ofBiFunction<T, U, R>. Thereducemethod uses this to accumulate stream elements into a single result. - Static Method Reference Compatibility: The method references
Integer::maxandInteger::minmatch the signatureint apply(int a, int b), thus compatible with theBinaryOperator<Integer>interface.
Key Points Summary
| Concept / Method | Description |
| Streams | Java 8 feature for processing sequences of elements. |
| Reduction | Combines elements into a single result. |
| Method Reference | Integer::max and Integer::min point to static methods.
Used as BinaryOperator. |
| reduce Method | Accepts a BinaryOperator to reduce stream. |
| max & min Methods | Use Comparator for stream element comparison. |
Additional Exploration
Comparator and Method References
While method references like Integer::max can't be directly used with the max and min methods that require comparators, custom comparators can use lambda expressions:
Handling Empty Streams
Using reduce or finding the max and min on streams returns an Optional because streams may be empty. Handling this scenario is crucial:
Concluding Thoughts
The use of .max(Integer::max) and .min(Integer::min) in Java 8 streams showcases the power of method references and functional programming in Java. By leveraging functional interfaces like BinaryOperator, developers can write concise and expressive code to process and aggregate data in streams efficiently. As you explore the Streams API further, keep in mind the flexibility and adaptability of Java's lambda expressions and method references in functional programming.
Related reading
- Why do most fields class members in Android tutorial start with m?
- Why do people say that Java can't have an expression evaluator?
- Why do people still use primitive types in Java?
- Why do this() and super() have to be the first statement in a constructor?
- Why do we need a private constructor?
- Why do we use finally blocks?
- Why does a Java class compile differently with a blank line?
- Why does appending to a String save memory?

OOD Fundamentals
Master object-oriented design from first principles, SOLID, design patterns, and classic interview problems with hands-on coding.
View the courseTrack what you have practised
A free account saves your progress, solutions and study plan across every problem on Codemia.
Interview Questions practice on Codemia
Over 8,000 real interview questions from top companies, searchable by company and role.