java-8 中有界通配符的规则更改?

rule changing in bounded wildcards in java-8?

我正在学习 Java 中关于泛型的教程,定义了这个静态方法:

public static <T extends Comparable<T>> T min(T a) { ... }

并说

min(new GregorianCalendar());

无法编译,因为 GregorianCalendar extends CalendarCalendar implements Comparable<Calendar> 所以它暗示 GregorianCalendar implements Comparable<Calendar> 而不是 Comparable<GregorianCalendar>。 所以为了编译签名必须改成:

public static <T extends Comparable<? super T>> T min(T a) { ... }

这完全可以理解。该方法的第一个版本实际上不会在 java-5 中编译,但它会在 java-8 中编译! (我尝试了 5 到 8)

为什么 java-8 现在允许这样做? (因为它现在使它更加混乱)。这背后的新 "rule" 是什么?

类型推断!

JLS §18. Specifically, I'll direct you to JLS §18.2(第 678 页)中有大量关于此的信息,其中指出:

在你的情况下,让 S = GregorianCalendarT = Calendar。 This page states (during the reduction process) if S is a sub-type of T, then S is considered to be of type T (GregorianCalendar被视为 Calendar).

你的例子的问题是你写了

min(new GregorianCalendar());

作为一个独立的表达式。在 Java 8 之前,这将由编译器通过推断 GregorianCalendar 来处理 T,它不满足约束“… extends Comparable<T>”,因此它被拒绝。

从 Java 8 开始,编译器为 T 推断出 Calendar,它满足约束条件,传递 GregorianCalendar 的实例是有效的,其中 Calendar 是预期的,所以这个例子被接受了。这就像你在以前的 Java 版本中写了 YourClass.<Calendar>min(new GregorianCalendar());

然而,这并没有解决 min 声明不接受 TGregorianCalendar 的根本问题。虽然 Java 8 允许您编写,例如

Calendar c = min(new GregorianCalendar());

T使用Calendar,对T使用GregorianCalendar仍然无效,例如

GregorianCalendar c = min(new GregorianCalendar());

还是会被编译器拒绝

因此,还是需要使用带通配符的声明

public static <T extends Comparable<? super T>> T min(T a) { ... }

允许呼叫者像

一样使用它
GregorianCalendar c = min(new GregorianCalendar());