Consider the two following methods. Their only difference is in their generic type declaration of Function<>
public static <T, U extends Comparable<? super U>> Comparator<T> comparing(
Function<? super T, ? extends U> keyExtractor)
{
Objects.requireNonNull(keyExtractor);
return (Comparator<T> & Serializable)
(c1, c2) -> keyExtractor.apply(c1).compareTo(keyExtractor.apply(c2));
}
public static <T, U extends Comparable<? super U>> Comparator<T> comparingT(
Function<T, ? extends U> keyExtractor) <-- Check here! T instead of ? super T
{
Objects.requireNonNull(keyExtractor);
return (Comparator<T> & Serializable)
(c1, c2) -> keyExtractor.apply(c1).compareTo(keyExtractor.apply(c2));
}
Let's say I have a List<GamingComputer> = { ASUS, MSI }
, where GamingComputer
extends Computer
. Now, I want to sort them.
List.sort( comparing( Computer::getProperty ) )
What is the type of T?
My intuition: T=GamingComputer
. comparing()
takes in keyExtractor
, whose type is Function<Computer super GamingComputer, Property>
. Finally, comparing()
returns Comparator<GamingComputer>
.
This code, proving my intuition, compiles perfectly:
Function<Computer, Property> f1 = Computer::getProperty;
Comparator<GamingComputer> c1 = comparing(f1);
Now, by PECS, since c1
, c2
are being added to a collection/ constructor/ method, as long as the collection handles their parent class, it could handle any child class. That's the reason behind <? super T>
.
As demonstrated in this code:
Function<Computer, Property> f2 = Computer::getProperty;
Comparator<GamingComputer> c2 = comparingT(f2); // FAILS to compile. Required Type: Comparator<GamingComputer>, Provided Comparator<Computer>
Comparator<Computer> c2 = comparingT(f2); // compiles successfuly
Since f2
works with all Computer
, it should be able to work with any GamingComputer
as well. However, because we did not declare type as <? super T>
, we are unable to construct a Comparator
of GamingComputers
.
Makes sense. Then...
Comparator<GamingComputer> c22 = comparingT(Computer::getProperty); // compiles successfuly... WT, excuse mi French, heck???
My guess: comparingT()
with type T=GamingComputer
forces a downcast on keyExtractor
, which is Computer::getProperty
. It forces all Computers
to use GamingComputer::getProperty
, which is probably not an issue, since Comparator<GamingComputer>
does compare GamingComputers
.
But, why does this NOT compile?
Function<Computer, Property> f22 = GamingComputer::getProperty;
The error is very peculiar:
Non-static method cannot be referenced from a static context, which is probably a bug from Intellij
Non-static method cannot be referenced from a static context in java 8 streams
Still, when compiling:
java: incompatible types: invalid method reference
method getPart in class function.GamingComputer cannot be applied to given types
required: no arguments
found: function.Computer
reason: actual and formal argument lists differ in length