Java 比较器 class 中的泛型。 T 和 U 的确切类型是什么?

Generics in Java Comparator class. What is the exact type of T and U?

考虑以下两种方法。它们唯一的区别在于它们的泛型类型声明 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));
}

假设我有一个 List<GamingComputer> = { ASUS, MSI },其中 GamingComputer 扩展 Computer。现在,我想对它们进行排序。

List.sort( comparing( Computer::getProperty ) )

T的类型是什么?

我的直觉:T=GamingComputercomparing()接受keyExtractor,其类型为Function<Computer super GamingComputer, Property>。最后,comparing()returnsComparator<GamingComputer>.

这段代码,证明了我的直觉,编译完美:

Function<Computer, Property> f1 = Computer::getProperty;
Comparator<GamingComputer> c1 = comparing(f1);

现在,通过 PECS,由于 c1c2 被添加到集合/构造函数/方法中,只要集合处理它们的 parent class ,它可以处理任何 child class。这就是 <? super T>.

背后的原因

如代码所示:

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

因为 f2 适用于所有 Computer,它应该也适用于任何 GamingComputer。但是,因为我们没有将类型声明为<? super T>,所以我们无法构造GamingComputersComparator

有道理。那么...

Comparator<GamingComputer> c22 = comparingT(Computer::getProperty); // compiles successfuly... WT, excuse mi French, heck???

我的猜测:comparingT() 类型 T=GamingComputer 强制对 keyExtractor 进行向下转换,即 Computer::getProperty。它强制所有 Computers 使用 GamingComputer::getProperty,这可能不是问题,因为 Comparator<GamingComputer> 确实比较 GamingComputers.

但是,为什么这不能编译?

Function<Computer, Property> f22 = GamingComputer::getProperty;

错误很奇特:

Non-static 方法无法从静态上下文中引用,这可能是来自 Intellij

bug

仍然,编译时:

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

My intuition: T=GamingComputer

你的直觉是正确的。


Why does Comparator<GamingComputer> c22 = comparingT(Computer::getProperty); compile?

这是因为与不变的功能接口实例不同,方法引用是协变和逆变的。使用 here 中的示例,您可以执行以下操作:

// in SomeClass
public static Integer function(Object o) {
    return 2;
}

// ...
Function<String, Object> function = SomeCLass::function;

或者使用您的 类,您可以:

Function<GamingComputer, Property> f = Computer::getProperty;

“就好像”方法引用在它们的参数上有 ? super 并且在 return 类型上有 ? extends! Java 语言规范的 15.13.2 部分详细说明了哪些有效,哪些无效。

所以对于 c22T 仍然是 GamingComputer。方法引用 Computer::getProperty 可以转换为 Function<GamingComputer, Property> 它是一个方法引用。

这不会编译,即使 f2“存储”Computer::getProperty:

Comparator<GamingComputer> c2 = comparingT(f2);

因为f2本身不是方法引用。它是一个变量。

Why does Function<Computer, Property> f22 = GamingComputer::getProperty; not compile?

f22 可以接受任何类型的 Computer,因为它接受 Computer。如果给 f22 另一种计算机(不是 GamingComputer),GamingComputer.getProperty 肯定无法处理,是吗?