试着理解泛型



我有以下一段代码

public static void main(String[] args) {
        ArrayList<Integer> iList = new ArrayList();
        iList = returList();
        for (int i = 0; i < iList.size(); i++) {
            System.out.println(iList.get(i));
        }
    }
    public static ArrayList returList() {
        ArrayList al = new ArrayList();
        al.add("S");
        al.add(1);
        return al;
    }

现在我的问题是,为什么Arraylist接受行"Arraylist iList=new Arraylist();"中的原始Arraylist对象创建并且在相同的情况下甚至从方法调用返回偶数。

现在,会有哪种类型的数据,Generics会暗示吗?我没有看到任何编译错误,这段代码运行得很好。

由于Java泛型的实现方式(请参阅类型擦除以获得介绍性解释),可以创建泛型类的"原始"类型实例,然后将它们强制转换为泛型版本,就像在分配给iList时一样。这会导致编译器发出警告,因为这是一个潜在的不安全操作。您可以将任何您喜欢的类型添加到原始ArrayList(它相当于ArrayList),但如果随后将其强制转换为更具体的泛型类型,则可能会产生不一致的集合。

在您的示例中,您在returList中创建了这样一个列表。然而,您的代码并没有表现出这一点,因为println()不依赖于传递给它的列表元素的类型

Integer val = iList.get(i);

到您的for循环并运行您的代码,当您的程序尝试将字符串"s"强制转换为Integer时,您将获得ClassCastException

当泛型集合不一致时,除非尝试使用泛型类型访问不一致的元素,否则您不会发现。

阅读以下内容:http://java.sun.com/j2se/1.5/pdf/generics-tutorial.pdf,建议两到三次。

创建泛型是为了帮助开发人员,但您必须使用它们才能受益。你的IDE会警告你ArrayList:的原始用法

ArrayList is a raw type. References to generic type ArrayList<E> should be parameterized

但这并不能阻止你上吊自杀。请注意,在某些IDE中,你实际上可以强制这些都是编译错误,这可能就是你想要的。

现在,就列表中的内容而言,它正是您放入的内容。请记住,泛型只不过是"语法糖",即从生成的类中完全删除的编译器提示。因此,在运行时,并没有指示对象具有什么泛型类型。在您的情况下,您的代码运行良好,因为您所做的只是打印出列表内容。您的所有对象都将自动转换为字符串!试试这个来获得一些乐趣和游戏:

public static void main(String[] args) {
        ArrayList<Integer> iList = new ArrayList();
        iList = returList();
        for (final Integer i: iList) {
            System.out.println(i.intValue());
        }
}

因为这是错误的。这是正确的方式:

public static void main(String[] args) {
    List<Integer> iList = returList();
    for (int i = 0; i < iList.size(); i++) {
        System.out.println(iList.get(i));
    }
}
public static List<Integer> returList() {
    List<Integer> al = new ArrayList<Integer>();
    //al.add("S"); This line can't compile now!
    al.add(1);
    return al;
}

注意事项:

  1. 针对接口编程
  2. 避免不必要的初始化
  3. 在实现上也使用类型参数(赋值运算符的RHS)

这里有答案:

为什么允许原始类型

原始类型主要是为了方便接口具有非通用(遗留)代码。

例如,如果您有一个非通用的遗留方法,它将List作为参数,可以将参数化类型(如List)传递给这种方法。相反,如果您有一个返回List的方法,可以将结果分配给List类型的引用变量,前提是您知道由于某种原因,返回的列表实际上是字符串列表。

了解一些泛型的历史可能有助于解释其中的一些奇怪之处。

当最初为Java提出泛型时,假设必须在不更改底层.class格式的情况下引入这些更改。因此泛型被实现为原始类型之上的薄层。一种名为类型擦除的技术成为了解决方案。这意味着编译器将删除所有泛型信息,以便在运行时没有任何泛型信息可用。

如果您不能依赖源代码中显示的类型,则会产生令人讨厌的后果。

这就是您的示例中发生的情况。

请参阅http://docs.oracle.com/javase/tutorial/java/generics/erasure.html了解更多详细信息。

请注意,对于Java5,.class格式最终进行了更改,以适应自动装箱。由于时间限制,类格式正在改变,从而允许正确地进行泛型,这一事实没有得到利用。

最新更新