在没有泛型前,从集合中读取的元素都必须进行强制转换,比如:
List foos = new ArrayList();
foos.add(new Foo());
Foo = (Foo)foos.get(0); //必须强制转换
否则连编译没法通过,但是有了泛型之后,编译器可以在插入数据的时候自动帮你转换,编译时告知是否插入了错误类型。
List<Foo> foos = new ArrayList<Foo>();
foos.add(new Foo());
Foo = foos.get(0); //无需转换
首先需要知道几个术语:
举例:
public class MyType<E> {
class Inner { }
static class Nested { }
public static void main(String[] args) {
MyType mt; // warning: MyType is a raw type
MyType.Inner inn; // warning: MyType.Inner is a raw type
MyType.Nested nest; // no warning: not parameterized type
MyType<Object> mt1; // no warning: type parameter given
MyType<?> mt2; // no warning: type parameter given (wildcard OK!)
}
}
这里,mt和inn就是原生类型的,而ntst不是,因为他是静态类型成员,mt1和mt2属于参数化类型。
本质上,原生类型的行为方法和泛型没什么两样,就像如下代码:
List names = new ArrayList(); // warning: raw type!
names.add("John");
names.add("Mary");
names.add(Boolean.FALSE); // not a compilation error!
代码没有任何问题,但是,
for (Object o : names) {
String name = (String) o;
System.out.println(name);
} // throws ClassCastException!
// java.lang.Boolean cannot be cast to java.lang.String
如上代码在运行的时候,就会抛出ClassCastException的异常。
有了泛型,编译器就能帮你完成类型检测的工作.
List<String> names = new ArrayList<String>();
names.add("John");
names.add("Mary");
names.add(Boolean.FALSE); // compilation error!
原生类型List躲避了泛型检查,参数化类型 List 告知编译器,它能装任意类型的对象,虽然你可以将List传递给类型List的参数,但是不能将它传递给类型List 的参数。为什么呢?这是子类型化(subtyping)的规则。
常规类中,如果类B继承A,那么类B就是类A的子类,但是这条规则并不能适用于泛型中。
class A {}
class B extends A {}
List<B> lb = new ArrayList<>();
List<A> la = lb; //compile-time error
[generics-listParent.gif] 尽管Integer是Number的子类,但是List并不是List的子类,两者没有任何关系,而他两的共同父类是List<?>.
为了通过List的元素来访问Number的方法,可以使用向上通配符:
List<? extends Integer> intList = new ArrayList<>();
List<? extends Number> numList = intList //Ok,List<? extends Integer> is a subtype of List<? extends Number>
