List<T> 不等于 List<T>?

8

看一下这个简单的Java泛型示例:

class List<T> {
    T head;
    List<T> next;
}

class A<T> {
    List<T> l;

    public <T> int length() {
        List<T> l = this.l;
        int c = 1;
        while (l.next != null) {
            c++;
            l = l.next;
        }
        return c;
    }

    public static void main(String[] args) {
        A<Integer> a = new A<Integer>();
        a.l = new List<Integer>();
        a.l.head = 123;
        a.l.next = new List<Integer>();
        a.l.next.head = 432;
        System.out.println("list length: " + a.length());
    }
}

编译时出现错误,提示类型不兼容,但声称这两个变量是相同的类型:

$ javac A.java && java A
A.java:10: incompatible types
found   : List<T>
required: List<T>
        List<T> l = this.l;
                        ^
1 error

如果我将length()的第一行更改为List<T> l = (List<T>)(Object)this.l;,它就可以工作了。为什么?

1
创建一个与常见的JDK类(在这个例子中是java.util.List)名称冲突的类是一个糟糕的想法。这会引发混乱。 - yshavit
1个回答

19

您在泛型类中使用了以下代码声明了一个泛型方法:

public <T> int length() {

这个 <T> 和你的类的 <T> 是不同的。根据JLS第6.3节

类的类型参数(§8.1.2)的作用域是类声明的类型参数部分,类声明中任何超类或超接口的类型参数部分以及类体。

在你的方法上不需要重新声明 <T>,因为类的类型参数已经在作用域内。

要使用你的类的泛型类型参数 <T>,只需让你的方法不声明另一个 <T>,直接使用你的类的 <T> 即可:

public int length() {

要理解为什么你的投掷有效:

List<T> l = (List<T>)(Object)this.l;
您可以将任何对象转换为Object,然后将结果强制转换为List<T>。您始终可以将其转换为任何想要的内容;如果在运行时它实际上不是List,Java将会抛出ClassCastException异常。但编译器也会发出警告,因为它无法保证这个<T>是原始的<T>
为了说明<T>之间的差异,您可以在方法的泛型类型参数中使用<U>,并获得相同的结果:
public <U> int length() {
    List<U> l = (List<U>)(Object)this.l;

这段代码编译时会出现与类型安全相关的警告。

如果你确信类型安全能够得到保证,可以使用@SuppressWarnings("unchecked")来注释你的方法。但是在这里,我建议完全移除方法中的通用类型参数,并且使用类的类型参数。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接