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

爪哇

我有一Storage堂课:

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

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

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 {

}

驱动类:

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是泛型类,对其方法的操作是有意义的;我知道案例 1 是如何工作的。

在第 2 种情况下,使用上面的maxin签名GenericTest,我收到编译错误:

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

我知道这Comparable<Child>不是Comparable<Parent>(Typed params are invariant)的子类型

所以,我将签名更新为

// 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

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

不是extends关于T本身及其子类的类型吗?

编辑:
Java 8 带有更新的类型推断规则。以下内容足以用于 Java 8 及更高版本中的类型推断,但不适用于 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 来说已经足够了。

跳跳虎

为了使事情更清楚,类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类可以看作是:“找到Child实现 a Comparableof 自身的第一个超类
与情况 1) 一样,它不可能是Child因为它不是Comparable<Child>. 自身Child实现的第一个(也是唯一一个)超类ComparableParent.

我无法理解如何更新List<T>List<? extends T>使编译器推断类型Parent

  • List<T>被迫T成为Child
  • List<? extends T>强迫?成为Child,不是T

本文收集自互联网,转载请注明来源。

如有侵权,请联系 [email protected] 删除。

编辑于
0

我来说两句

0 条评论
登录 后参与评论

相关文章

Eclipse Java 编译器推断出错误的泛型类型?

编译器无法推断泛型类构造函数类型的原因是什么?

Typescript编译器使用泛型推断类型

为什么编译器不推断泛型

声明泛型类型时的编译器警告

解决泛型类型约束中的编译器错误

在Java中转换为泛型方法类型变量时出现编译器警告

如何让编译器推断出nullptr类型?

调用泛型方法java的编译器错误

当运行时类型也是泛型类型时,将方法引用用作期望使用泛型类型接口的方法的lambda时,编译器错误

通用lambda的编译器推断类型

为什么编译器在具有双界泛型类型时不将 T(interface) 推断为实现 T 的类?

如何在泛型方法中进行类型推断?

调用泛型方法时如何推断参数类型?

当指定类型的运算符重载存在时,C#编译器如何确定泛型方法中的引用相等性?

Java泛型-编译器错误

泛型混淆:欺骗编译器

如何使F#编译器将我的参数推断给泛型函数?

Scala方法推断的泛型类型

泛型方法类型参数推断问题

从泛型推断类型

泛型方法和泛型类中的类型推断

为什么C#编译器不认为这种泛型推断是模棱两可的?

在编译时推断泛型是否为数组类型

为什么Java编译器失去构造函数实例化轨道泛型类型的?

条件类型添加了额外的泛型编译器错误

为什么编译器不对扩展接口的泛型强制执行返回类型值?

使用泛型时,javac编译器会为每种类型创建不同的类吗?

Java泛型错误:来自命令行编译器的不可转换类型