Why Stream.max(Integer::max) compiles?

Consider the following code:

Stream<Integer> stream = Stream.of(1,2,3,4);
int max = stream.max(Math::max).get();
System.out.println(max);

According to the Javadoc of Steam.max(), the argument for the method max() should be a Comparator. In the code above, the method reference is to static methods of the Math class. But the code compiles without any warning or error messages.

So why is Math::max acceptable while it’s apparently not a Comparator?

The reason why the code above compiles is in the way of how the new lambda functionality works in Java 8. It relies on a concept which is informally known as “single abstract method (SAM)” interfaces. The basic idea is that any interface with one abstract method can be automatically implemented by any lambda or method reference, if the lambda or method reference matches the SAM in the interface.

If we take a look at the function interface Comparator[1], its single abstract method looks like the following:

public Comparator<T> { 
       int compare(T o1, T o2); 
}

If a method (max() in this case) is looking for a Comparator, then it’s essentially looking for this signature:

int xxx(Integer o1, Integer o2);

“xxx” means the method name is not used for matching purposes. The Math.max() method has the following signature:

int max(int i1, int i2);

Autoboxing allows this to appear as a Comparator in a method context. Therefore, Math::max is accepted. Similarly, a bunch of other method is also acceptable, such as Integer::max, Integer::min, Math::min, etc.

The result will be wrong. Instead, Integer::compareshould used like the following:

max = stream.max(Integer::compare).get();
System.out.println(max);

References:
[1]. Javadoc of Comparator in Java 8
[2]. This question is also posted by Fge from Stack Overflow. The most popular answer is posted by David Lloyd.

Leave a Comment