以下是代码,
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
的数组。
..
lst
拥有Object[0]
数组。
因此,如果类型Object
的数组由构造函数创建,那么javac
如何在此语句String s = lst.get(0);
中看不到类型转换问题,尽管在调用构造函数时使用了通用语法?
答案 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
,因此将不安全类型处理限制为特定位置,其中实现者可以确保正确处理强制转换。