今天将Effective Java(第二版)中的泛型部分读完,深感自己泛型掌握有多么不熟练,还是需要多加练习。
废话少说,上点重点:
1.不要使用原型
比如:
List list = new ArrayList();
当你用该list引用指向其他带有泛型的List时,是不会出现编译错误的,只会给一个rawtype的警告,但是————
这很容易出现挂羊头卖狗肉的情况,比如你指向了一个List<Integer>,却add了一个String,这不会出现任何编译错误,但
当你取出来转成Integer时就会出现ClassCastException,因为你根本存的就是一个String...
如果你不确定类型,那么请使用“?”这种通配符,可以消除警告,并且阻止除null外的一切元素添加。
2.优先考虑列表,而不是数组
原因很简单,数组是可协变的,泛型不是。比如,你有一个List<String>[]数组(假设合法),如果你用一个Object[]引用指向了这个泛型数组,
这肯定可以,因为数组是可协变的,但是当你从Object[]中取出这个元素,并调用get方法返回Integer时,就会出现类型转换的异常。所以,泛型
数组是非法的。
3.优先考虑泛型及泛型方法
这个不用多说,自己可以看看集合类的源码,基本上都是使用了泛型,对于数组存储这块,应该是利用反射,用Arrays.newInstance()方法生成一个
Object引用的数组,然后再强制转换为T[]的数组,这样可以保证在add或者get时,类型的正确性(编译器帮你搞定)。
泛型方法是非常强大的方法:
public static <T> void swap(List<T> list, int i, int j)
这种泛型方法,可以自动捕获异常,包括你传入的参数为List<?>时也可以,只不过这时候捕获的为?这种通配符,建议只在void方法中使用。
4.PECS原则
即T的生产者(producer)泛型为<? extends T>,T的消费者(consumer)泛型为<? super T>。
原因很简单,生产者主要提供的方法为get()方法,而Java在运行时泛型是被擦除的,也就是这里的T变为了Object,
在调用或离开方法的“边界处”,编译器会按照泛型参数T转型,也就是说,调用生产者get方法所获取的对象类型,都被一致
地转为T类型,表明不论是什么类型,获取出来只要T类型能接就可以了(为T的子类型)。消费者主要提供的方法为add()
(或者其他的什么consume()等等),这时表明消费者消费的类型只要是T的父类型即可,这是调用add()方法时,所消费的对象
均为T或T的父类型,保证了下限。
总结:要更好的理解泛型,必须多动手去练,去思考,抓住泛型在运行时是被擦除这一特点,就能掌握大部分泛型的使用了
原文地址:https://www.cnblogs.com/cedriccheng/p/8908499.html