一提到泛型,大家首先想到的就是。在集合中加入泛型,来规定集合中元素的类型。那么,什么是泛型?为什么要使用泛型?泛型除了集合以外,还可以在其他地方使用吗?我们今天就来了解一下。
1、为什么要使用泛型?
首先,我们来看一下ArrayList集合类的定义:
|
|
可以看到,ArrayList底层是使用数组实现的。而且,该数组是Object数组。这就意味着,在该数组中,我们可以存放任何类型的元素
|
|
元素存放进集合后,我们遍历数组
|
|
运行代码,在2处,会抛出java.lang.ClassCastException。这是类型转换异常。
原因是,在1处,我们先取出集合元素,创建了Object变量obj。按照多态的概念,父类变量可以指向任何一个子类对象。而Object是所有类的父类,所以变量obj可以指向任何一个子类对象。所以,1处的代码实际是:
|
|
很明显,在2处,将obj类型强转为String类型时,对于第一个元素是可以的。第一个元素指向的是字符串对象。但第二个元素是Integer对象,第三个元素是JFrame对象。将这样两个对象强转为String类型,当然会转换失败。
在以上代码中,存在的问题在于,ArrayList集合元素定义的是Object数组。该数组中可以存放任何类型的对象。所以,我们并不清楚集合元素,到底指向的是什么样的对象。所以,在使用集合元素时,会先进行类型转换,再调用方法。这时,如果不清楚集合中存放元素的类型,那么,就很容易抛出java.lang.ClassCastException类型转换异常。
那么能不能规定,集合中只能存放什么类型的元素,从而在使用集合元素时,开发者可以清晰的知道集合元素的类型,因此避免这样的类型转换异常呢?泛型,可以解决这些问题。
2、什么是泛型?
泛型,大家可以理解为“参数化类型”。也就是,可以将一个类中的某些属性的数据类型、方法参数类型、返回类型,都以变量方式表示。在使用/调用时传入具体的类型。
我们来看一下,集合中是如何规定集合元素类型的。
|
|
注意这个E。这可以看做是参数化的类型,ArrayList中采用泛型化定义之后,
例如:
|
|
集合中解决类型转换异常的思路是:通过对象中泛型的定义,在添加元素时,规定只能添加某个类型的元素。如果添加其他类型的对象,那么编译错误。这样,取集合元素时,当然也只能取出同一类型的对象。这样就避免了类型转换异常。
所以,从在加上泛型定义的集合中取出元素时,无需进行类型转换,直接可以定义泛型变量接收集合元素。同样的两个ArrayList集合,也可以通过不同泛型的定义,从而规定存放集合的元素类型。
|
|
我们也可以给一个类或一个接口,定义多个泛型参数。
例如,Map集合定义如下:
|
|
定义Map对象时,我们可以给其中的键对象K,和值对象V,指明具体的类型。那么,凡是引用K类型的属性类型、方法参数,方法返回类型,就必须和定义Map对象时的K类型一致。同样,凡是引用V类型的属性类型、方法参数、方法返回类型,也必须和定义Map对象时的V类型一致。
|
|
3、自定义泛型
那么,泛型的定义是否只限于集合框架呢?当然不是,凡是需要定义类型参数的类、接口、方法都可以使用泛型。
例如:集合框架中有一个工具类:Collections。该类中提供了一个sort排序方法,定义如下:
|
|
sort方法在给集合元素排序时,需要由开发者定义排序的规则。这样,要由开发者实现Comparator接口,从而定义集合元素的比较规则。
不过,开发者在实现Comparator接口时,需要指出,是给什么样类型元素定义比较规则。
Comparator接口定义如下:
|
|
开发者在实现接口时,定义了Comparator比较器泛型类型后,compare方法参数的类型,就变成了定义比较器泛型的类型。
|
|
开发者在重写compare()方法时,就可以定义两个Student对象比较的规则了。
从以上内容,大家已经明白了泛型的具体运作过程。也知道了接口、类和方法也都可以使用泛型去定义。在具体使用时,可以分为泛型接口、泛型类和泛型方法。
在实际开发中,我们也可以通过泛型的定义,来规范一些属性、方法参数,以及返回类型。
例如,我们在做分页时,可以将页面显示的数据、总记录数和总页数,封装成专门用于分页的实体类。
|
|
在需要分页的方法中,以CutPageBean做为返回类型。不过,在设计时,为了让开发者更清楚的知道,list属性中存放元素的类型,我们可以定义泛型去规范。
|
|
这就意味着,创建CutPageBean时,规定了泛型类型后,list属性中存放元素的类型,就和定义CutPageBean对象时的类型一致。
|
|
这样,在设计业务接口方法,以及实现时,可以明确的知道应该将什么样的对象,存放进分页实体对象定义的集合属性中。
总结:
1、泛型,表示参数化类型。可以将一个类中的某些属性的数据类型、方法参数类型、返回类型,都以变量方式表示。在使用/调用时传入具体的类型。
2、可以给一个类或一个接口,定义一个或多个泛型参数。使用的类型和泛型定义的类型一致。
3、泛型,我们也可以根据业务需要进行自定义。接口、类和方法也都可以使用泛型去定义。