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

目标类型具有通配符时的泛型方法类型推断

燕靖
2023-03-14
问题内容

我了解编译器使用目标类型来确定使通用方法调用适用的类型参数。例如,在以下语句中:

List<String> listOne = Collections.emptyList();

其中的签名中Collections.emptyList具有类型参数T

public static final <T> List<T> emptyList() {

在这种情况下,推断出的类型参数TString

现在考虑以下几点:

List<?> listTwo = Collections.emptyList();

在这种情况下,推断的类型是什么?是Object吗 还是因为通配符告诉编译器任何类型都是可能的?


问题答案:

通配符的每种用法都有与之关联的不同类型。(通常,JLS将此称为“新鲜类型”。)例如,这就是这样的编译器错误的工作方式:

List<?> list0 = ... ;
List<?> list1 = ... ;
list0.add(list1.get(0)); // error

因为在这种情况下,编译器会给list0list1提供单独的类型,因此在大多数情况下

reference_type_of(List<?>) != reference_type_of(List<?>)

如果您尝试类似的操作,则可以开始了解它如何适合于推论

{
    List<?> list0 = ... ;
    List<?> list1 = ... ;
    test(list0, list1);
}
static <T> void test(List<T> list0, List<T> list1) {}

编译器发出错误的地方实际上告诉我们有关为list0和生成的类型的一些信息list1

错误:类Ideone中的方法测试无法应用于给定类型;
    测试(列表0,列表1);
    ^
  必需:List <T>,List <T>
  **找到:List <CAP#1>,List <CAP#2>**
  原因:不存在类型变量T的实例,因此
          参数类型List <CAP#2>符合形式参数类型List <T>
  其中T是类型变量:
    T扩展在方法<T> test(List <T>,List <T>)中声明的对象
  **其中CAP#1,CAP#2是新鲜的类型变量:
    CAP#1扩展了对象的捕获范围
    CAP#2扩展了对象的捕获范围**

(我的粗体为粗体。)这些CAP#...类型是在捕获转换期间生成的。它向我们展示的是,当检查方法调用的表达,list0list1相互给予不同的类型。(对于需要对错误进行解释的用户:这是因为声明的test断言两个列表必须具有相同的代码T。)

因此,由于我们现在知道通配符与引用类型相关联,因此我们可以看到

List<?> empty = Collections.emptyList();

该调用将被推断为类似“上限为Object的新鲜类型”之类的东西。或象征性地,我们可以说编译器可能会看到类似

// target type       -->       inferred invocation type
//     v                           v
List<CAP#1> empty = Collections.<CAP#1>emptyList();

虽然:当然,我们总是会猜测一点,因为这取决于编译器如何实现。从理论上讲,对于类似上面的琐碎分配的情况emptyList(),它不必做任何工作来生成正确的字节码。

另外,很抱歉,我今天不喜欢规格检查。从本质上讲,这里的类型推断通过生成一组约束来证明方法调用应该或不应该编译,从而起作用。18.5.2中描述的算法以这种方式合并了通配符。



 类似资料:
  • 问题内容: 我了解编译器使用目标类型来确定使通用方法调用适用的类型参数。例如,在以下语句中: 其中的签名中具有类型参数 在这种情况下,推断出的类型参数是。 现在考虑以下几点: 在这种情况下,推断的类型是什么?是吗 还是因为通配符告诉编译器任何类型都是可能的? 问题答案: 通配符的每种用法都有与之关联的不同类型。(通常,JLS将此称为“新鲜类型”。)例如,这就是这样的编译器错误的工作方式: 因为在这

  • 问题内容: Java通常可以基于参数(甚至与返回类型(例如,与C#相反))来推断泛型。 恰当的例子:我有一个通用类,它只存储一对值,并且可以按以下方式使用: 该方法如下所示: 非常好。但是,这不再适用于以下需要通配符的用例: (请注意显式强制转换以进行正确的类型。) 代码失败,并显示以下错误(由Eclipse提供): 类型不匹配:无法从转换为 但是,显式调用构造函数仍然可以按预期工作: 有人可以解

  • 在了解Java泛型的过程中,我遇到了以下问题: 假设我有下面的方法来添加列表的元素,只限于包含数字的列表。 但是这段代码和这段代码有什么不同: 它们都按预期编译和执行。这两者之间有什么区别?除了语法之外?什么时候我更喜欢使用通配符而不是前者? 是的,使用通配符方法,我不能在列表中添加除null之外的新元素,否则它将无法编译。除此之外呢?

  • 本文向大家介绍Java8中对泛型目标类型推断方法的改进,包括了Java8中对泛型目标类型推断方法的改进的使用技巧和注意事项,需要的朋友参考一下 一、简单理解泛型 泛型是Java SE 1.5的新特性,泛型的本质是参数化类型,也就是说所操作的数据类型被指定为一个参数。通俗点将就是“类型的变量”。这种类型变量可以用在类、接口和方法的创建中。 理解Java泛型最简单的方法是把它看成一种便捷语法,能节省你

  • 问题内容: 我正在为核心数据编写通用包装类。 这是我的一些基本类型。没什么特别的。 我已经将我的coredata写在协议中抽象化了。如果您让我知道您对我要提出的抽象的意见,我将不胜感激。但是在扩展中,我遇到了以下错误: 无法将类型“ NSFetchRequest”的值转换为预期的参数类型“ NSFetchRequest <_>” 不确定我该如何解决。我尝试了各种更改代码的尝试,但未成功…… 另外,

  • 问题内容: 我想拥有一个Class对象,但是我想强迫它代表的任何类扩展A类并实现接口B。 我可以: 要么: 但我不能两者都做。有没有办法做到这一点? 问题答案: 实际上,你可以做你想做的事。如果要提供多个接口或一个类加接口,则必须使通配符看起来像这样: 请参见sun.com上的泛型教程,特别是页面底部的“ 绑定类型参数”部分。实际上,如果需要,你可以列出多个接口,并& InterfaceName根