目标类型具有通配符时的通用方法类型推断 [英] Generic method type inference when the target type has a wildcard

查看:459
本文介绍了目标类型具有通配符时的通用方法类型推断的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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

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

其中 Collections.emptyList 有一个类型参数 T 在其签名中

  public static final< T>列表与LT; T> emptyList(){

在这种情况下, T 字符串



现在考虑以下几点:

 列表<?> listTwo = Collections.emptyList(); 

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

解决方案

通配符的每个用法都有与之相关的独特类型。 (通常JLS将这称为新鲜类型)。这就是例如这样的编译器错误的作用:

 列表与LT;?> list0 = ...; 
列表<?> list1 = ...;
list0.add(list1.get(0)); //错误

因为 list0 和 list1 被编译器赋予不同的类型,这样大多数情况下

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

如果您尝试类似于

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

一个错误实际上告诉了我们一些关于为 list0 list1 产生的类型。

 
错误:类Ideone中的方法测试不能应用于给定的类型;
test(list0,list1);
^
required:List< T>,List< T>
found:List< CAP#1>,List< CAP#2>
原因:没有类型变量T的实例存在,所以
参数类型列表< CAP#2>符合形式参数类型List< T>
其中T是一个类型变量:
T extends在方法< T> test(List< T>,List< T>)中声明的Object
< CAP#2是新鲜的类型变量:
CAP#1扩展了Object从捕获的?
CAP#2扩展了对象来捕获?

(我强调以粗体显示。) CAP#... 类型是在捕获转换。它向我们展示的是,当检查方法调用表达式时, list0 和 list1 被赋予了彼此不同的类型。 (对于那些需要对错误进行解释的人:这是因为 test 声明声明两个列表必须具有相同的 T

因为我们现在知道一个通​​配符与一个引用类型相关联,所以我们可以看到,在像

 列表<?> empty = Collections.emptyList(); 

调用将被推断为类似上限为Object的新鲜类型。或者象征性地我们可以说编译器可能会看到类似于

  // target type  - >推断的调用类型
// v v
List< CAP#1> empty = Collections。< CAP#1> emptyList();

虽然:当然我们总是猜测一下,因为它取决于编译器如何实现这个。理论上,对于像 emptyList()这样简单的赋值的情况,它不需要工作来产生正确的字节码。



另外,我很抱歉,我今天不喜欢spec spec淘宝。基本上,这里的类型推断通过生成一组约束来演示方法调用应该或不应该编译。在 18.5.2中描述的算法以这种方式合并通配符。


I understand that the compiler uses the target type to determine the type argument that makes the generic method invocation applicable. For instance, in the following statement:

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

where the Collections.emptyList has a type parameter T in its signature

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

In this case, the inferred type argument for T is String.

Now consider the following:

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

What is the inferred type in this case? Is it Object? Or it doesn't really matter due to the wildcard telling the compiler any type is possible?

解决方案

Each usage of a wildcard has a distinct type associated with it. (Usually the JLS refers to this as being a "fresh type".) This is how for example a compiler error like this works:

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

Because it's the case that list0 and list1 are given separate types by the compiler such that for the most part

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

You can begin to see how this fits in to type inference if you try something like

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

Where the compiler emits an error that actually tells us a little bit about the types that it has generated for list0 and list1.

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 ?

(My emphasis in bold.) These CAP#... types were generated during capture conversion. What it's showing us is that when the method invocation expression was examined, list0 and list1 were given distinct types from each other. (And for those that need an explanation for the error: it's because the declaration of test asserts that both Lists must have the same T.)

So since we now know that a wildcard gets associated a reference type, we can see that in a case like

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

The invocation will be inferred as something like "a fresh type where the upper bound is Object". Or symbolically we could say the compiler might see something like

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

Although: of course we are always guessing a little bit because it's up to the compiler how it implements this. In theory, for a case like the above trivial assignment of emptyList(), it would not have to do work to generate the correct bytecode.

Also, I am sorry, I don't feel like spec scouring today. Essentially the type inference here works by generating a set of constraints to demonstrate that the method invocation should or should not compile. The algorithm described in 18.5.2 incorporates a wildcard in this way.

这篇关于目标类型具有通配符时的通用方法类型推断的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆