首页 > 解决方案 > Java Comparator 类中的泛型。T 和 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 }, where GamingComputerextends Computer。现在,我想对它们进行排序。

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

T的类型是什么?

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

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

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

现在,由于 PECS被添加到集合/构造函数/方法中c1c2只要集合处理它们的父类,它就可以处理任何子类。这就是背后的原因<? 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适用于 all Computer,因此它也应该能够适用于 any GamingComputer。但是,因为我们没有将 type 声明为<? super T>,所以我们无法构造 a Comparatorof GamingComputers

说得通。然后...

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

我的猜测:comparingT()with typeT=GamingComputer强制keyExtractorComputer::getProperty. 它强制所有人Computers使用GamingComputer::getProperty,这可能不是问题,因为Comparator<GamingComputer>确实 compare GamingComputers

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

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

错误非常奇特:

无法从静态上下文引用非静态方法,这可能是Intellij的错误

不能从 java 8 流中的静态上下文引用非静态方法

尽管如此,在编译时:

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

标签: javagenericscomparator

解决方案


我的直觉:T=GamingComputer

你的直觉是正确的。


为什么要Comparator<GamingComputer> c22 = comparingT(Computer::getProperty);编译?

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

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

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

或者使用您的课程,您可以执行以下操作:

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

这是“好像”方法引用? super在它们的参数和? extends返回类型上!Java 语言规范的第15.13.2节详细说明了哪些有效,哪些无效。

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

这不会编译,即使f2"stores" Computer::getProperty

Comparator<GamingComputer> c2 = comparingT(f2);

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

为什么不Function<Computer, Property> f22 = GamingComputer::getProperty;编译?

f22将能够接受任何一种Computer,因为它接受Computer。如果你给f22另一种电脑(不是GamingComputer),GamingComputer.getProperty肯定不能处理,不是吗?


推荐阅读