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

发布于 2024-11-28 18:25:39 字数 2392 浏览 3 评论 0原文

对于 Java 中的参数化类型,检查参数是否在其绑定范围内的规则如何完全适用于通配符?

给定一个这样的类:

class Foo<T extends Number> {}

试验编译器接受的内容得知:

  • <代码>?允许使用不相关的接口类型的 extends 通配符:Foo 有效
  • A ?不允许使用不相关的类类型来扩展通配符:Foo 无效。 没有任何类型可以同时是 NumberThread 的子类型
  • 这是有道理的,因为在 中 ? super 通配符,通配​​符中的下界必须是类型变量的下界的子类型: Foo,因为 Runnable 不是 Number 的子类型。再说一次,这个限制是完全有道理的。

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

来自 JLS 的该部分:

参数化类型由类或接口名称 C 和实际类型参数列表组成。如果 C 不是泛型类或接口的名称,或者实际类型参数列表中类型参数的数量与 C 声明的类型参数的数量不同,则会出现编译时错误。对于类或接口类型,我们也包括通用版本,除非明确排除。在本节中,令 A1 , ... , An 为 C 的形式类型参数,并令 Bi 为 Ai 的声明边界。符号 [Ai := Ti] 表示用类型 Ti 替换类型变量 Ai,即 1 <= i <= n,并在本规范中使用。

令P=G<T1,...,Tn>是参数化类型。情况一定是,在 P 进行捕获转换(第 5.1.10 节)后,产生类型 G,对于每个实际类型参数 Xi,1 <= i <= n , Xi <: Bi[A1 := X1, ..., An := Xn] (§4.10),或者发生编译时错误。

将其应用于 P = Foo:给出 C = Foo,n = 1,T1 = ?超级可运行和B1 = 数字

对于捕获转换,捕获转换定义的这一部分 适用:

如果 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]),因此该参数在其范围内。 (但事实并非如此!)

按照同样的推理,每个通配符都在其范围内,所以这里有些东西是不对的......但是这个推理到底错在哪里?如果正确应用,这些规则如何发挥作用?

With parameterized types in Java, how do the rules that check if a parameter is within its bound work exactly for wildcards?

Given a class like this:

class Foo<T extends Number> {}

Experimenting with what the compiler accepts learns that:

  • A ? extends wildcard using an unrelated interface type is allowed: Foo<? extends Runnable> is valid
  • A ? extends wildcard using an unrelated class type is not allowed: Foo<? extends Thread> is invalid. That makes sense because no type can be a subtype of both Number and Thread
  • In a ? super wildcard, the lower bound in the wildcard must be subtype of the bound of the type variable: Foo<? super Runnable> is not allowed because Runnable is not a subtype of Number. Again, this restriction makes perfect sense.

But where are these rules defined? Looking at the Java Language Specification section 4.5, I don't see anything distinguishing interfaces from classes; and when applying my interpretation of the JLS Foo<? super Runnable> is said to be valid. So I probably misunderstood something. Here's my attempt:

From that section of the JLS:

A parameterized type consists of a class or interface name C and an actual type argument list <T1 , ... , Tn>. It is a compile time error if C is not the name of a generic class or interface, or if the number of type arguments in the actual type argument list differs from the number of declared type parameters of C. In the following, whenever we speak of a class or interface type, we include the generic version as well, unless explicitly excluded. Throughout this section, let A1 , ... , An be the formal type parameters of C, and let be Bi be the declared bound of Ai. The notation [Ai := Ti] denotes substitution of the type variable Ai with the type Ti, for 1 <= i <= n, and is used throughout this specification.

Let P = G<T1, ..., Tn> be a parameterized type. It must be the case that, after P is subjected to capture conversion (§5.1.10) resulting in the type G<X1, ..., Xn>, for each actual type argument Xi, 1 <= i <= n , Xi <: Bi[A1 := X1, ..., An := Xn] (§4.10), or a compile time error occurs.

Apply that to P = Foo<? super Runnable>: that gives C = Foo, n = 1, T1 = ? super Runnable and B1 = Number.

For capture conversion this part of the definition of capture conversion applies:

If Ti is a wildcard type argument of the form ? super Bi, then Si is a fresh type variable whose upper bound is Ui[A1 := S1, ..., An := Sn] and whose lower bound is Bi.

That gives G<X1, ..., Xn> = Foo<X> where X is a fresh type variable with upper bound Number and lower bound Runnable. I don't see anything explicitly forbidding such a type variable.

There are no type variables in B1 = Number, so Bi[A1 := X1, ..., An := Xn] is still simply Number.
X has Number as upper bound (coming from the capture conversion), and according to the subtyping rules "The direct supertypes of a type variable are the types listed in its bound", so X <: Number (= Bi[A1 := X1, ..., An := Xn]), so this parameter is within its bounds. (But it isn't!)

Following the same reasoning every wildcard is within its bounds, so something here isn't right... But where exactly did this reasoning go wrong? How do these rules work when applied correctly?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(1

神魇的王 2024-12-05 18:25:39

关于泛型的 JLS 并不完整,您发现了其中的另一个漏洞。类型变量的下限几乎没有被讨论,并且我在具有上限 Number 和下限 RunnableX 规范中也没有看到任何限制>。他们可能把它遗漏了。

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

很容易检查上限和下限是否构成空类型集。下界的所有超类型都是已知的;其中至少一个应该是上限,否则没有任何类型同时位于两个边界内。

--

两个 Foo 的情况在规范中得到了很好的定义。通过捕获转换,我们有了新的类型变量 X ,其上限为 A & Number,规范表示上限 V1&...&Vm

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

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

JLS on generics is incomplete, and you caught another hole in it. Lower bound on type variables is barely discussed, and I don't see any restriction in spec either on X having upper bound Number and lower bound Runnable. They probably left it out.

Intuitively, there must be at least one possible type that satisfies both upper bound and lower bound of a type variable, otherwise the variable and all types using the variable would be useless. Since this is almost certainly a programming mistake, compile should fail.

It's easy to check whether upper bound and lower bound make an empty set of types. All super types of the lower bound are known; at least one of them should be the upper bound, otherwise there is no type that's within both bounds.

--

The two Foo<? extends A> cases are well defined in the spec. With capture conversion, we have new type variable X with upper bound A & Number, and the spec says for an upper bound V1&...&Vm

It is a compile-time error if for any two classes (not interfaces) Vi and Vj,Vi is not a subclass of Vj or vice versa.

Therefore if A=Thread, capture conversion fails.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文