我们先来看一段代码:
public static void main(String[] args) { Collection collection = new ArrayList<>(); collection.add("hello"); collection.add("hello"); String[] objects = (String[]) collection.toArray(); System.out.println(Arrays.toString(objects)); }
执行结果如下:
对于这样的代码,我们在不了解泛型的理解是认为,在我们new出来collection之后,我们放的两个元素都是String类型的元素,因此我们就会想到取得时候利用强制类型转换,转换为String类型的数组去取,但是这段代码的底层原理远比我们想的要复杂的多,toArray()方法的返回值是一个Object【】数组,此处的强制类型实际上是将这个Object【】类型的数组强制类型转换为String【】数组,实际上,Object【】类型数组当中的每个元素并没有进行强制类型转换,因此,就会发生强制类型转换异常,只是运行时所抛出的异常,编译时期并不会察觉到。
我们再看一段代码,再来进行思考:
class MyArrayList{ private int[] elem; private int usedSize; public MyArrayList(){ this.elem = new int[10]; } public void add(int val){ this.elem[usedSize] = val; usedSize++; } public int get(int pos){ return this.elem[pos]; } }
通过上面的代码我们思考:
(1)只能存放整数的类型,不通用
(2)能不能啥都可以放?String、Boolean....
我们对代码做出以下调整:
class MyArrayList{ private Object[] elem; private int usedSize; public MyArrayList(){ this.elem = new Object[10]; } public void add(Object val){ this.elem[usedSize] = val; usedSize++; } public Object get(int pos){ return this.elem[pos]; } }
这样就又回到了最开始的问题,存放元素时,不能指定元素,什么都可以放,并且当我们取出数据的时候,都需要进行强制类型转换。
那么:
1、能不能我指定这个顺序表的类型?
2、指定类型之后,是不是就只能放指定类型的数据呢?
3、取出数据能不能不进行强制类型转换?
解决方案——泛型
泛型的意义:
1、自动对类型进行检查
2、自动对类型进行强制类型转换
泛型的核心操作:将类型作为参数进行传递。
我们先看代码如何实现:
class MyArrayList<E>{ private E[] elem; private int usedSize; public MyArrayList(){ this.elem = (E[]) new Object[10]; } public void add(E val){ this.elem[usedSize] = val; usedSize++; } public Object get(int pos){ return this.elem[pos]; } }
我们指定传递的类型为String类型,注意,此处传递的类型只能是包装类型,不能是基本数据类型,当我们放置整数类型时,就会自动帮我们检查到错误并提示我们不能放置整数类型,换句话说,泛型是编译时期的一种机制(擦除机制)。
什么又是擦除机制呢?
所谓擦除机制,就是在编译时,将所有的E类型替换为Object类型。(具体细节我们在深入理解泛型时描述)。
我们可以参考标准库当中的做法来理解泛型:
总结:当我们new一个泛型集合类时,传递的类型参数并不参与类型的组成,在当我们获取元素时,就会根据传递的类型参数进行强制类型转换,最后将强制类型转换后的结果返回。获取时根据传递的参数对数据进行强制类型转换。