java泛型语法如何帮助避免类型转换?

时间:2015-07-11 05:55:58

标签: java generics javac

以下是代码,

import java.util.List;
import java.util.ArrayList;


public class Dummy {
    public static void main(String[] args) {
        List<String> lst = new  ArrayList<String>();
        lst.add("a string");
        lst.add("another string");
        String s = lst.get(0);
    } //end main
}

当调用构造函数new ArrayList<String>();时,会创建类型为Object的数组。

enter image description here ..

lst拥有Object[0]数组。

enter image description here

因此,如果类型Object的数组由构造函数创建,那么javac如何在此语句String s = lst.get(0);中看不到类型转换问题,尽管在调用构造函数时使用了通用语法?

2 个答案:

答案 0 :(得分:2)

以下是一些非通用代码:

public class MyList {

    List myData = new ArrayList();

    public void add(Object ob) {
        myData.add(ob);
    }

    public Object getAtIndex(int ix) {
        return myData.get(ix);
    }
}

此代码允许您将任何类型的对象存储到MyList实例中,即使 MyList的合同指定对象必须全部属于同一类型。此外,如果使用MyList实例存储String实例,则在检索它们时必须手动将它们转换为String。

String myString = (String) myList.get(1);

上面的MyList类不是类型安全的。如果除了String实例之外的对象存储在MyList实例中(这可能在运行时发生而没有任何抱怨),则上述赋值语句绝对可能会因ClassCastException而失败。

这是一个通用的MyList类:

public class MyList<T> {

    List<T> myData = new ArrayList<>();

    public void add(T ob) {
        myData.add(ob);
    }

    public T getAtIndex(int ix) {
        return myData.get(ix);
    }
}

现在,编译器保证只能从MyList实例添加和检索T个实例。由于编译器保证始终返回T个实例,因此您可以使用这样的语法而无需任何手动转换:

String myString = myList.get(1);

生成的MyList类现在是类型安全的。编译器不允许您将除T个实例之外的任何内容存储到MyList实例中,这样可确保在运行时不会发生ClassCastExceptions。如果检查字节码,您会发现编译器已自动放置了一个强制转换。

Java中的泛型是仅编译时现象。在字节码中,上面MyList类中对T的所有引用都被Object替换。此过程称为&#34;类型擦除&#34;。重要的是要记住Java中的类型安全性仅由编译器提供。如果您的程序编译时没有任何错误 AND 而没有任何警告,那么您的程序,泛型和所有类型都是类型安全的。但是,已编译的程序几乎没有保留有关通用参数的信息。

答案 1 :(得分:0)

从课堂上走得更远:

@SuppressWarnings("unchecked")
E elementData(int index) {
    return (E) elementData[index];
}

public E get(int index) {
    rangeCheck(index);

    return elementData(index);
}

@SuppressWarnings告诉编译器你确定你在那里执行的未经检查的强制转换。由于所有其他操作(例如get(int))都使用类型参数E,因此将不安全类型处理限制为特定位置,其中实现者可以确保正确处理强制转换。