Javagenerics类型中的通配符参数在其范围内的正式条件是什么?

使用Java中的参数化类型,检查参数是否在其绑定工作中的规则如何完全针对通配符?

鉴于这样的类:

class Foo {} 

试验编译器接受的内容可以了解:

  • 一个? extends 允许使用不相关的接口类型? extends通配符: Foo Foo有效
  • 一个? extends 不允许使用不相关的类类型? extends通配符: Foo Foo无效。 这是有道理的,因为没有类型可以是NumberThread的子类型
  • 在一个? super ? super通配符,通配​​符的下限必须是类型变量的边界的子类型: Foo 不允许使用Foo因为Runnable不是Number的子类型。 同样,这种限制非常有意义。

但这些规则在哪里定义? 查看Java语言规范部分4.5 ,我没有看到任何区别于类的接口; 在应用我对JLS Foo解释Foo Foo据说是有效的。 所以我可能误解了一些东西。 这是我的尝试:

从JLS的那一部分:

参数化类型由类或接口名称C和实际类型参数列表组成。 如果C不是generics类或接口的名称,或者实际类型参数列表中的类型参数的数量与C的已声明类型参数的数量不同,则是编译时错误。在下文中,每当我们说话时除了明确排除之外,我们还包括类或接口类型的通用版本。 在本节中,让A1,…,An成为C的forms类型参数,并且让Bi为AI的声明边界。 符号[Ai:= Ti]表示用类型Ti替换类型变量Ai,对于1 <= i <= n,并且在整个说明书中使用。

令P = G 为参数化类型。 必须是这样的情况,在P经历捕获转换(第5.1.10节)之后导致类型G ,对于每个实际类型参数Xi,1 <= i <= n, Xi <:Bi [A1:= X1,…,An:= Xn](§4.10),或发生编译时错误。

将其应用于P = Foo Foo :得到C = Foo ,n = 1,T1 = ? super Runnable ? super Runnable和B1 = Number

对于捕获转换,此部分捕获转换定义适用:

如果Ti是表单的通配符类型参数? super Bi,则Si是一个新型变量,其上限为Ui [A1:= S1,…,An:= Sn],其下限为Bi。

这给出了G = Foo其中X是一个新的类型变量,上限为Number ,下限为Runnable 。 我没有看到任何明确禁止这种类型变量的东西。

B1 = Number中没有类型变量,因此Bi [A1:= X1,…,An:= Xn]仍然只是NumberXNumber作为上限(来自捕获转换),并根据子类型规则 “类型变量的直接超类型是其绑定中列出的类型”,因此X <: Number (= Bi [A1:= X1] ,…,An:= Xn]),因此该参数在其范围内。 (但事实并非如此!)

按照相同的推理, 每个通配符都在其范围内,所以这里的某些东西是不对的……但是这个推理到底出错的地方呢? 如果正确应用,这些规则如何运作?

关于generics的JLS是不完整的,你抓住了另一个漏洞。 类型变量的下限几乎没有讨论过,我没有看到规格上的任何限制,无论是具有上限Number X还是下限的Runnable 。 他们可能把它排除在外。

直观地,必须至少有一种可能的类型满足类型变量的上限和下限,否则变量和使用该变量的所有类型都将是无用的。 由于这几乎肯定是编程错误,编译应该失败。

很容易检查上限和下限是否为空类型。 下界的所有超类型都是已知的; 至少其中一个应该是上限,否则没有类型在两个边界内。

两个Foo Foo案例在规范中有明确定义。 通过捕获转换,我们有新的类型变量X ,上限为A & Number ,规格表示上限为V1&...&Vm

如果对于任何两个类(不是接口)Vi和Vj,Vi不是Vj的子类,反之亦然,则是编译时错误。

因此,如果A = Thread,则捕获转换失败。