Java Comparator类中的泛型。T和U的确切类型是什么?

huangapple go评论164阅读模式
英文:

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) <-- 在这里检查使用了 T 而不是 ? 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() 接受了类型为 Function<Computer super GamingComputer, Property>keyExtractor。最终,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); // 无法编译通过。所需类型:Comparator<GamingComputer>,提供的类型:Comparator<Computer>
Comparator<Computer> c2 = comparingT(f2); // 可以成功编译通过

因为 f2 对所有的 Computer 都适用,它也应该适用于任何 GamingComputer。然而,由于我们没有将类型声明为 <? super T>,所以我们无法构建一个 GamingComputers 的比较器。

有道理。那么...

Comparator<GamingComputer> c22 = comparingT(Computer::getProperty); // 可以成功编译通过... 什么鬼,原谅我的用词

我猜测:使用类型 T=GamingComputercomparingT()keyExtractor 上强制进行了一个向下转型,它是 Computer::getProperty。它强制所有的 Computer 使用 GamingComputer::getProperty,这可能不是问题,因为 Comparator<GamingComputer> 确实可以比较 GamingComputers

但是,为什么这个代码无法编译通过?

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

这个错误非常奇怪:

无法从静态上下文中引用非静态方法,这可能是 IntelliJ 的一个 bug。

链接:https://stackoverflow.com/questions/42200958/non-static-method-cannot-be-referenced-from-a-static-context-in-java-8-streams

然而,当编译时:

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
英文:

Consider the two following methods. Their only difference is in their generic type declaration of Function<>

public static &lt;T, U extends Comparable&lt;? super U&gt;&gt; Comparator&lt;T&gt; comparing(
        Function&lt;? super T, ? extends U&gt; keyExtractor)
{
    Objects.requireNonNull(keyExtractor);
    return (Comparator&lt;T&gt; &amp; Serializable)
        (c1, c2) -&gt; keyExtractor.apply(c1).compareTo(keyExtractor.apply(c2));
}
public static &lt;T, U extends Comparable&lt;? super U&gt;&gt; Comparator&lt;T&gt; comparingT(
        Function&lt;T, ? extends U&gt; keyExtractor) &lt;-- Check here! T instead of ? super T
{
    Objects.requireNonNull(keyExtractor);
    return (Comparator&lt;T&gt; &amp; Serializable)
        (c1, c2) -&gt; keyExtractor.apply(c1).compareTo(keyExtractor.apply(c2));
}

Let's say I have a List&lt;GamingComputer&gt; = { 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&lt;Computer super GamingComputer, Property&gt;. Finally, comparing() returns Comparator&lt;GamingComputer&gt;.

This code, proving my intuition, compiles perfectly:

Function&lt;Computer, Property&gt; f1 = Computer::getProperty;
Comparator&lt;GamingComputer&gt; 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 &lt;? super T&gt;.

As demonstrated in this code:

Function&lt;Computer, Property&gt; f2 = Computer::getProperty;
Comparator&lt;GamingComputer&gt; c2 = comparingT(f2); // FAILS to compile. Required Type: Comparator&lt;GamingComputer&gt;, Provided Comparator&lt;Computer&gt;
Comparator&lt;Computer&gt; 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 &lt;? super T&gt;, we are unable to construct a Comparator of GamingComputers.

Makes sense. Then...

Comparator&lt;GamingComputer&gt; 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&lt;GamingComputer&gt; does compare GamingComputers.

But, why does this NOT compile?

Function&lt;Computer, Property&gt; 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

https://stackoverflow.com/questions/42200958/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

答案1

得分: 1

我的直觉:T=GamingComputer

你的直觉是正确的。


为什么Comparator&lt;GamingComputer&gt; c22 = comparingT(Computer::getProperty);能够编译?

这是因为与不变的功能接口实例不同,方法引用是协变和逆变的。使用来自这里的示例,您可以做如下操作:

// 在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&lt;GamingComputer, Property&gt;,因为它是一个方法引用。

即使f2“存储”了Computer::getProperty,但以下代码不会编译:

Comparator<GamingComputer> c2 = comparingT(f2);

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

为什么Function&lt;Computer, Property&gt; f22 = GamingComputer::getProperty;无法编译?

f22将能够接受任何类型的Computer,因为它接受Computer。如果您给f22另一种类型的计算机(而不是GamingComputer),GamingComputer.getProperty肯定无法处理那种情况,不是吗?

英文:

> My intuition: T=GamingComputer

Your intuition is correct.

<hr>

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

This is because unlike instances of functional interfaces which are invariant, method references are covariant and contravariant. Using an example from here, you can do something like:

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

// ...
Function&lt;String, Object&gt; function = SomeCLass::function;

Or using your classes, you can do:

Function&lt;GamingComputer, Property&gt; f = Computer::getProperty;

It's "as if" method references have ? super on their parameters and ? extends on the return types! The details of what works and what doesn't are specified in section 15.13.2 of the Java Language Specification.

So for c22, T is still GamingComputer. The method reference Computer::getProperty can be converted to Function&lt;GamingComputer, Property&gt; it's a method reference.

This does not compile, even though f2 "stores" Computer::getProperty:

Comparator&lt;GamingComputer&gt; c2 = comparingT(f2);

because f2 is not a method reference itself. It is a variable.

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

f22 would be able to accept any kind of Computer, since it accepts Computer. If you give f22 another kind of computer (not GamingComputer), GamingComputer.getProperty certainly would not be able to handle that, would it?

huangapple
  • 本文由 发表于 2020年9月9日 10:10:42
  • 转载请务必保留本文链接:https://go.coder-hub.com/63803822.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定