当前位置: 首页 > 知识库问答 >
问题:

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

蒋茂材
2023-03-14

我知道编译器使用目标类型来确定使泛型方法调用适用的类型参数。例如,在以下声明中:

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

其中集合。emptyList的签名中有一个类型参数T

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

在本例中,T的推断类型参数是String

现在考虑以下几点:

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

这种情况下的推断类型是什么?它是对象吗?或者因为通配符告诉编译器任何类型都是可能的,所以这并不重要?


共有2个答案

韩阳云
2023-03-14

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

在某种程度上,这是一个哲学问题,因为类型参数对编译的字节码没有任何影响,所以具体是什么并不重要。唯一重要的是它是否不可能满足边界和上下文。只要编译器能够证明存在某种可以工作的类型,那么在我看来,它应该能够继续编译,而不需要拿出实际的类型。

经嘉
2023-03-14

通配符的每次使用都有一个与之关联的独特类型。(通常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) {}

其中,编译器发出一个错误,实际上告诉我们一点它为list0list1生成的类型。

error: method test in class Ideone cannot be applied to given types;
    test(list0, list1);
    ^
  required: List<T>,List<T>
  found: List<CAP#1>,List<CAP#2>
  reason: no instance(s) of type variable(s) T exist so that
          argument type List<CAP#2> conforms to formal parameter type List<T>
  where T is a type-variable:
    T extends Object declared in method <T>test(List<T>,List<T>)
  where CAP#1,CAP#2 are fresh type-variables:
    CAP#1 extends Object from capture of ?
    CAP#2 extends Object from capture of ?

(我用粗体强调。)这些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根