编译器如何在泛型方法上推断类型?

How is compiler inferring type on a generic method?

我有一个Storage class:

class Storage<E> {
    void add(E e) {
        // add element
    };

    void addAll(Iterable<? extends E> src) {
        for (E e : src)
            add(e);
    }
}

有两个 classes 其中 class Child extends Parent:

Parent

class Parent implements Comparable<Parent> {

    @Override
    public int compareTo(Parent o) {
        return 0; // some comparison logic
    }
}

Child

class Child extends Parent {

}

Driver class:

import java.util.Arrays;
import java.util.List;

public class GenericTest {

    public static void main(String[] args) {
        /******** CASE 1 *********/
        Storage<Parent> ds = new Storage<Parent>();
        ds.add(new Parent());
        
        ds.addAll(Arrays.asList(new Parent()));
        
        // Type params are invariant.
        // But List<Child> is possible due to bounded wildcard type on addAll
        ds.addAll(Arrays.asList(new Child())); // Makes sense
        
        /******** CASE 2 *********/
        List<Child> t = Arrays.asList();
        max(t);
        
    }
    
    static <T extends Comparable<T>> T max(List<T> list) {
        return null; // Return null so code can compile
    }
}

因为Storage是泛型class,对其方法的操作是有意义的;我知道情况 1 是如何工作的。

在情况 2 中,在 GenericTest 中使用上述 max 的签名,我得到编译错误:

The method max(List<T>) in the type GenericTest is not applicable for the arguments (List<Child>)

我知道 Comparable<Child> 不是 Comparable<Parent> 的 sub-type(类型化参数是不变的)。

所以,我将签名更新为

// update Comparable<T> to Comparable<? super T>
static <T extends Comparable<? super T>> T max(List<T> list)

现在,代码编译并推断签名为

<Child> Child GenericTest.max(List<Child> list)
这是有道理的。

当我将签名更新为

// update List<T> to List<? extends T>
static <T extends Comparable<T>> T max(List<? extends T> list)

代码编译并推断签名为

<Parent> Parent GenericTest.max(List<? extends Parent> list)

我无法理解将 List<T> 更新为 List<? extends T> 是如何让编译器推断类型 Parent 的。

我的意思是,对于泛型方法(使用 Child 类型直接调用),? extends T 如何帮助编译器引用 parent class Child?请注意,此签名具有 Comparable<T>(而不是 Comparable<? super T)。

extends 不是关于 T 本身及其 sub-classes 的类型吗?

编辑:
Java 8 带有更新的 type-inference 规则。 以下对于 Java 8 及更高版本中的 type-inference 已足够,但不适用于 Java 7 及以下版本:

static <T extends Comparable<T>> T max(List<? extends T> list)

它给出了编译错误:

Bound mismatch: The generic method max(List<? extends T>) of type GenericTest is not applicable for the arguments (List<Child>). The inferred type Child is not a valid substitute for the bounded parameter <T extends Comparable<T>>

签名

static <T extends Comparable<? super T>> void max(List<T> list) 
不过

足够 Java 7 了。

为了让事情更清楚,class Child:

  • extends Parent
  • implements Comparable<Parent>
1) static <T extends Comparable<T>> T max(List<T> list)
  • TChild
  • 失败,因为 Child 不是 Comparable<Child>
2) static <T extends Comparable<? super T>> T max(List<T> list)
  • TChild
  • ?Parent
  • 之所以有效,是因为 Child implements Comparable<Parent super Child>
3) static <T extends Comparable<T>> T max(List<? extends T> list)
  • TParent
  • ?Child
  • 之所以有效,是因为 Child extends Parent implements Comparable<Parent>

这里的情况3),找到一个有效的T class可以看做:”找到Child的第一个superclass实现了自身的 Comparable".
与情况 1) 一样,它不能是 Child,因为它不是 Comparable<Child>Child 的第一个(也是唯一一个)实现自身 Comparable 的超级 class 是 Parent.

I couldn't understand how updating List<T> to List<? extends T> made the compiler infer the type Parent.

  • List<T> 强制 TChild
  • List<? extends T> 强制 ?Child,而不是 T

对我来说,JDK 8 和 JDK 7 都失败了,所以我没有看到你在这些 JDks

之间描述的不一致

JDK 8 编译

JDK 7 编译

至于失败的原因叫做PECS(Producer Extends Consumer Super)

following answer 对我的理解帮助很大。

If you say <? extends SomeType>, then you wanna describe a ‘box’ that is the same size or smaller than the ‘SomeType’ box.

考虑到上述情况,当您将 max 方法描述为

 static <T extends Comparable<T>> T max(List<? extends T> list)

根据您的声明 List<? extends T> list,您需要一个与 T 相同或小于 T 的容器。您使用它时可以是 Child。

List<Child> t = Arrays.asList();

所以这应该已经编译了。

然而 return 类型是 <T extends Comparable<T>> T 然后被翻译成 Child extends Comparable<Child>.

Java 但是有一个限制,阻止您使用不同的类型参数实现相同的通用接口(在您的情况下是可比较的)。

这就是错误显示

的原因
required: java.util.List<? extends T>   
found: java.util.List<Child>   reason: inferred type does not
conform to declared bound(s)
inferred: Child
bound(s): java.lang.Comparable<Child>

解决方案

您应该使用 <T extends Comparable<? super T>>

声明 return 类型

Contra-variance: ? super T ( the family of all types that are supertypes of T) - a wildcard with a lower bound. T is the lower-most class in the inheritance hierarchy. from a previous answer on PECS

在那种情况下,它将编译为将绑定设置为 Comparable<Parent>