修改Comparator.comparing源码后类型推断的编译差异问题咨询
comparing Method Let's break down exactly why these two snippets behave differently when we remove the ? super T bounded wildcard from the HypotheticComparators.comparing method's Function parameter.
First, let's clarify the modified method signature we're assuming here (since you mentioned the original JDK's comparing has ? super T which you've removed):
// Modified version (no ? super T on Function parameter) public static <T, U extends Comparable<? super U>> Comparator<T> comparing(Function<T, U> keyExtractor) { // Implementation logic }
In contrast, the standard JDK signature uses Function<? super T, U> for the key extractor, which allows accepting functions that work on parent types of T thanks to contravariance.
1. Why the Lambda Variable Assignment Fails to Compile
Let's look at your first snippet:
Function<PhysicalObject, Double> weight = p->p.getWeight(); Comparator<Car> c = HypotheticComparators.comparing(weight);
Here's what's happening:
- You've explicitly typed the
weightvariable asFunction<PhysicalObject, Double>. This type is fixed once assigned—the compiler can't re-interpret it later. - When passing
weighttocomparing, the method expects aFunction<Car, Double>(since we're creating aComparator<Car>, soT=Car). - In Java,
Functionis contravariant in its input type:Function<Parent, R>is a supertype ofFunction<Child, R>, not a subtype. That means you can't pass aFunction<PhysicalObject, Double>where aFunction<Car, Double>is required—they're not compatible types without the? super Twildcard. The wildcard tells the compiler "this function can accept any type that is a supertype of T", which would allowPhysicalObject(a supertype ofCar) to be valid. Without it, the type check fails.
2. Why the Method Reference Compiles Successfully
Now your working snippet:
Comparator<Car> c3_1 = HypotheticComparators.comparing(PhysicalObject::getWeight);
Method references have far more flexible type inference than pre-assigned lambda variables:
- The compiler uses target typing here: it knows we need a
Comparator<Car>, so it infersT=Carfor thecomparingmethod. This means the method expects aFunction<Car, Double>as the key extractor. PhysicalObject::getWeightis a method reference to a non-static method onPhysicalObject. SinceCar extends PhysicalObject(we can assume this, as you're callinggetWeight()on aCar), anyCarinstance can invoke this inherited method.- The compiler automatically adapts the method reference to match
Function<Car, Double>: it treats the reference as taking aCar(instead ofPhysicalObject) as input, which is safe becauseCaris a subtype ofPhysicalObjectand inherits the method. There's no fixed type assigned upfront—instead, the context (thecomparingmethod's requirements) shapes how the method reference is typed.
Key Takeaway
The core difference boils down to type rigidity:
- A lambda assigned to an explicitly typed variable has a fixed type that can't be re-adjusted to fit a different context.
- A method reference is a "type-flexible" construct that the compiler can adapt to the target type required by the surrounding code.
When you remove ? super T from the comparing method, you lose the ability to accept functions that operate on parent types of T. The pre-assigned lambda falls into this invalid category, but the method reference can be inferred to exactly match the required Function<T, U> type because of its inheritance compatibility.
内容的提问来源于stack exchange,提问作者Jiaming Li

