当前位置: 首页 > 面试题库 >

Java泛型类型中的通配符参数处于其范围之内的正式条件是什么?

蒋鸿文
2023-03-14
问题内容

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

给定这样的课程:

class Foo<T extends Number> {}

通过试验编译器接受的内容,可以了解到:

  • ? extends使用不相关的接口类型被允许通配符:Foo<? extends Runnable>是有效
  • 一个? extends使用一个不相关的类类型是不允许的通配符:Foo<? extends Thread>无效。这是有道理的,因为没有类型可以是Numberand 的子类型。Thread
  • ? super通配符中,通配符的下限必须是类型变量bound的子类型:Foo<? super Runnable>不允许,因为Runnable不是的子类型Number。同样,此限制是完全合理的。

但是这些规则在哪里定义?在Java语言规范4.5节中,我看不到任何将接口与类区分开的东西。在应用我对JLS的解释时,Foo<? super Runnable>据说是有效的。所以我可能误会了一些东西。这是我的尝试:

在JLS的该部分中:

参数化类型由类或接口名称C和实际类型参数列表 组成。如果C不是通用类或接口的名称,或者实际类型实参列表中的类型实参的数量与C声明的类型实参的数量不同,则这是编译时错误。除非明确排除,否则我们还包括通用版本的类或接口类型。在本节中,设A1,…,An为C的形式参数,设Bi为Ai的声明边界。符号[Ai:=
Ti]表示类型变量Ai被Ti替换为1 <= i <= n,并且在整个说明书中使用。

令P = G 为参数化类型。对于每个实际的类型自变量Xi,必须对P进行捕获转换(第5.1.10节),得到类型G
之后,1 <= i <= n, Xi <:Bi [A1:= X1,…,An:= Xn](第4.10节),否则会发生编译时错误。

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

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

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

给出G =
Foo<X>其中 X是具有上限 Number和下限的新鲜类型变量 Runnable。我看不到任何明确禁止此类类型变量的东西。

B1 =中没有类型变量Number,因此Bi [A1:= X1,…,An:= Xn]仍然很简单Number
X具有Number上限(来自捕获转换),并且根据子类型化规则
“类型变量的直接超类型是在其范围内列出的类型”,因此X<:Number(= Bi [A1:= X1,… ,An:=
Xn]),因此此参数在其范围内。(但是不是!)

遵循相同的推理, 每个 通配符都在其范围之内,所以这里有些不正确…但是,这种推理到底在哪里出错?如何 时,正确应用这些规则工作?


问题答案:

关于泛型的JLS是不完整的,您还发现了另一个漏洞。类型变量的下限几乎没有讨论,并且在规范上X对上限Number和下限都没有任何限制Runnable。他们可能忽略了它。

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

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

-

这两种Foo<? extends A>情况在规范中都有很好的定义。随着采集转换,我们有新的变量类型X有上限A & Number,并规范说为上限V1&...&Vm

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

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



 类似资料:
  • 我正在开发一个小的android演示应用程序,其中混合了一些java类和接口与kotlin。我想从一个名为MvpViewStateActivity的具有泛型类型参数的java类扩展: 其中只是一个空的java接口: MvpPresenter是这样一个java接口: 和SearchViewActivity(Java): 所以现在我创建了一个kotlin接口: 其中,Item是pojo数据类。 也是用

  • 问题内容: 我正在刷新有关Java泛型的知识。因此,我转向了Oracle的优秀教程……并开始为我的同事编写一个演示文稿。我在本教程中遇到了有关通配符的部分,内容为: 考虑以下方法,printList: printList的目标是打印任何类型的列表,但未能实现该目标- 它仅打印Object实例的列表;它不能打印,,,等等,因为它们不是的亚型。要编写通用的printList方法,请使用: 我知道那是行

  • 我正在刷新我关于Java泛型的知识。所以我转向甲骨文的优秀教程...并开始为我的同事准备一个演示文稿。我在教程中遇到了通配符部分,上面写着: 考虑以下方法,打印列表: printList的目标是打印任何类型的列表,但它无法实现该目标-它只打印对象实例的列表;无法打印列表 我明白那个

  • 这是我试图理解的编译器行为的一个过度简化版本: 在上面的代码片段中,是一个类型范围比HouseCat接口允许的类型范围更广的引用,即: 如果我尝试执行类似的操作,编译器会告诉我不满足类型参数的约束。那么,,至少是潜在的。 编译器不会让我创建违反类型参数约束的实例,但我对它使用作为引用上限的行为感到困惑。在(,)之间有一个无效的类型范围,那么编译器为什么不拒绝这个引用定义呢? 澄清:我的问题不是关于

  • 问题内容: 我试图理解Java泛型,它们似乎很难理解。例如,这很好… …就是这个… … 还有这个 … …但是不能编译: 有人可以用简单的语言解释发生了什么吗? 问题答案: 对于泛型类型,主要要了解的是它们不是协变的。 因此,尽管您可以这样做: 以下内容将无法编译: 这是为了避免您绕过通用类型的情况: 因此,一一讲解您的示例 1个 您的通用方法采用a ,而您采用;(基本上是)。可以分配给类型,并且编

  • 问题内容: 是否有将通用类型参数约束为 任何 类型范围的语法或解决方法? 我知道您可以将一个类型限制为 所有 类型的范围(即逻辑): 是否有逻辑版本,例如: 如果没有支持该语法的语法(我认为不存在),是否有一种好的方法或方法? 在某些情况下,一个示例用例可能是: 人们似乎迷上了上面我的方法示例的确切语义。让我们尝试一下: 编辑: 我不会在编译时知道(可能来自外部代码),所以我想避免每种类型都有具体