编译器对泛型类型的自动绑定(类型推断)

时间:2010-11-10 11:06:27

标签: java generics binding type-inference

以下代码在t3行中有编译错误:

public <E> List<E> getList()
{
    return new ArrayList<E>();
}
public <T> void first()
{
    List<T> ret = new ArrayList<T>();
    List<T> list = getList();
    T t1 = ret.get(0);
    T t2 = list.get(0);
    T t3 = getList().get(0);
}

错误消息是: 类型不匹配:无法从Object转换为T

我知道我可以使用强制转换或手动绑定来解决问题,我的问题是:编译器是否很难进行自动绑定,是否会出现故障? < / p>

编辑:添加了错误消息。

编辑:添加了另一个例子,说明错误是如何发生的。

编辑:删除了第二个例子,因为它令人困惑,使问题更清晰。

5 个答案:

答案 0 :(得分:5)

在第一种情况下,您有两个类型参数名为T的泛型方法,但这些类型参数不同,所以让我们为它们指定不同的名称:

public <E> List<E> getList() { ... }
public <T> void first() { ... }

然后它的工作原理如下:

  1. List<T>的元素(类型为T的对象)被分配给T类型的变量,所以一切正常:

     List<T> ret = new ArrayList<T>();   
     T t1 = ret.get(0);
    
  2. 首先,将List<E>类型的对象分配给List<T>。此语句正常工作,因为类型参数E是从赋值左侧的类型推断出来的,因此T = E。然后它像前面的情况一样工作:

     List<T> list = getList();          
     T t2 = list.get(0);
    
  3. 在这种情况下,您尝试将类型E的对象分配给T类型的变量,但无法推断E,因此假定为{{1所以赋值失败:

    Object

    您可以通过手动将 T t3 = getList().get(0); 绑定到E来解决此问题:

    T
  4. 对于泛型类 T t3 = this.<T>getList().get(0); ,您没有两个独立的类型参数,因此两个方法中的TestGenerics<T>指的是相同的类型。

答案 1 :(得分:4)

您正在使用泛型方法调用泛型方法。您需要将泛型参数从第一个方法传递给getList方法。

List<T> list = this.<T>getList();

T t3 = this.<T>getList().get(0);

从这两个编译中,第一个编译也没有给出泛型参数,因为编译器可以从列表类型中获取类型(从赋值的左侧)。在第二种情况下,它不是直接赋值,因此编译器不知道getList()调用的类型。

对于不同的编译器,这些行为可能会有所不同。

答案 2 :(得分:1)

我相信axtavt的答案比我自己更正确。由于我的内容包含了我的一些猜测,而另一个答案解释了所有观察到的行为,引用相关的来源并且一般意义上我请求您阅读axtavts答案。

为了完整起见,我会在这里留下原来的答案。只是不要把它当作绝对真理。


java.util.List.get的签名如下:

 public abstract java.lang.Object get(int arg0);
无论类型的参数化如何,

get()都会返回Object。这就是您无法将get(0)分配给T类型的变量的原因。即使您(实际上)可以保证列表中始终存在T,但接口也没有做出承诺,编译器也无法接受您的承诺。

问题似乎与类型擦除有关。编译此代码时,它可以正常工作:

public <T> void someMethod(java.util.List<T> list) {
    T s = list.get(0);
}

编译器知道它正在处理List<T>,并且在编译时可以使用List<T>的签名。它知道get()将返回T,并且非常高兴。如果您将代码更改为此代码则不再有效:

public <T> List<T> getList() {
    return new ArrayList<T>();
}

public <T> void someMethod() {
    T s = getList().get(0);
}

这可能是因为在编译getList()方法时,类型会被删除,现在它将返回非通用java.util.List类型。 get()上的List返回Object,编译器将不再知道它曾经是List<T>并会抱怨。

答案 3 :(得分:0)

尝试将其强制转换为T类型。

    public <T> List<T> getList()
    {
        return new ArrayList<T>();
    }
    public <T> void first()
    {
        List<T> ret = new ArrayList<T>();
        List<T> list = getList();
        T t1 = ret.get(0);
        T t2 = list.get(0);
        T t3 = (T) getList().get(0);
    }

答案 4 :(得分:0)

getList()方法似乎每次返回一个最初为空的新列表。从空列表中获取(0)将抛出IllegalArgumentException。这是一个运行时错误,但它可能不会导致编译错误。