这是一些非通用代码:
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 实例中even ifMyList 的契约指定对象必须全部属于同一类型。此外,如果您使用 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);
}
}
现在,编译器保证只有T
可以从 MyList 实例中添加和检索实例。因为编译器保证T
实例将始终被返回,您可以使用这样的语法而无需任何手动转换:
String myString = myList.get(1);
泛化的 MyList 类现在是类型安全的。编译器不允许您存储除以下内容之外的任何内容T
实例到 MyList 实例中,这保证了运行时不会发生 ClassCastExceptions。如果您检查字节码,您会发现编译器已自动进行强制转换。
Java 中的泛型是仅编译时现象。在字节码中,所有引用T
上面的 MyList 类中的替换为Object
。此过程称为“类型擦除”。请务必记住,Java 中的类型安全仅由编译器提供。如果你的程序编译没有任何错误AND如果没有任何警告,那么您的程序(包括泛型和所有内容)都是类型安全的。然而,编译后的程序几乎没有保留有关通用参数的信息。