You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

修改Comparator.comparing源码后类型推断的编译差异问题咨询

Why Lambda vs Method Reference Compile Differently in Modified 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 weight variable as Function<PhysicalObject, Double>. This type is fixed once assigned—the compiler can't re-interpret it later.
  • When passing weight to comparing, the method expects a Function<Car, Double> (since we're creating a Comparator<Car>, so T=Car).
  • In Java, Function is contravariant in its input type: Function<Parent, R> is a supertype of Function<Child, R>, not a subtype. That means you can't pass a Function<PhysicalObject, Double> where a Function<Car, Double> is required—they're not compatible types without the ? super T wildcard. The wildcard tells the compiler "this function can accept any type that is a supertype of T", which would allow PhysicalObject (a supertype of Car) 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 infers T=Car for the comparing method. This means the method expects a Function<Car, Double> as the key extractor.
  • PhysicalObject::getWeight is a method reference to a non-static method on PhysicalObject. Since Car extends PhysicalObject (we can assume this, as you're calling getWeight() on a Car), any Car instance can invoke this inherited method.
  • The compiler automatically adapts the method reference to match Function<Car, Double>: it treats the reference as taking a Car (instead of PhysicalObject) as input, which is safe because Car is a subtype of PhysicalObject and inherits the method. There's no fixed type assigned upfront—instead, the context (the comparing method'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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.20 07:54:21