转自 https://blog.youkuaiyun.com/qq_27093465/article/details/73229016
一、引入
1、泛型是什么
首先告诉大家ArrayList就是泛型。那ArrayList能完成哪些想不到的功能呢?先看看下面这段代码:- ArrayList<String> strList = new ArrayList<String>();
- ArrayList<Integer> intList = new ArrayList<Integer>();
- ArrayList<Double> doubleList = new ArrayList<Double>();
2、没有泛型会怎样
先看下面这段代码:我们实现两个能够设置点坐标的类,分别设置Integer类型的点坐标和Float类型的点坐标:
- //设置Integer类型的点坐标
- class IntegerPoint{
- private Integer x ; // 表示X坐标
- private Integer y ; // 表示Y坐标
- public void setX(Integer x){
- this.x = x ;
- }
- public void setY(Integer y){
- this.y = y ;
- }
- public Integer getX(){
- return this.x ;
- }
- public Integer getY(){
- return this.y ;
- }
- }
- //设置Float类型的点坐标
- class FloatPoint{
- private Float x ; // 表示X坐标
- private Float y ; // 表示Y坐标
- public void setX(Float x){
- this.x = x ;
- }
- public void setY(Float y){
- this.y = y ;
- }
- public Float getX(){
- return this.x ;
- }
- public Float getY(){
- return this.y ;
- }
- }
答案是可以的,因为Integer和Float都是派生自Object的,我们用下面这段代码代替:
- class ObjectPoint{
- private Object x ;
- private Object y ;
- public void setX(Object x){
- this.x = x ;
- }
- public void setY(Object y){
- this.y = y ;
- }
- public Object getX(){
- return this.x ;
- }
- public Object getY(){
- return this.y ;
- }
- }
在使用的时候是这样的:
- ObjectPoint integerPoint = new ObjectPoint();
- integerPoint.setX(new Integer(100));
- Integer integerX=(Integer)integerPoint.getX();
- integerPoint.setX(new Integer(100));
- Integer integerX=(Integer)integerPoint.getX();
同理,FloatPoint的设置和取值也是类似的,代码如下:
- ObjectPoint floatPoint = new ObjectPoint();
- floatPoint.setX(new Float(100.12f));
- Float floatX = (Float)floatPoint.getX();
比如我们改成下面这样,编译时会报错吗:
- ObjectPoint floatPoint = new ObjectPoint();
- floatPoint.setX(new Float(100.12f));
- String floatX = (String)floatPoint.getX();
- String floatX = (String)floatPoint.getX();
而在运行时,则不然,在运行时,floatPoint实例中明明传进去的是Float类型的变量,非要把它强转成String类型,肯定会报类型转换错误的!
那有没有一种办法在编译阶段,即能合并成同一个,又能在编译时检查出来传进去类型不对呢?当然,这就是泛型。
下面我们将对泛型的写法和用法做一一讲解。
二、各种泛型定义及使用
1、泛型类定义及使用
我们先看看泛型的类是怎么定义的:- //定义
- class Point<T>{// 此处可以随便写标识符号
- private T x ;
- private T y ;
- public void setX(T x){//作为参数
- this.x = x ;
- }
- public void setY(T y){
- this.y = y ;
- }
- public T getX(){//作为返回值
- return this.x ;
- }
- public T getY(){
- return this.y ;
- }
- };
- //IntegerPoint使用
- Point<Integer> p = new Point<Integer>() ;
- p.setX(new Integer(100)) ;
- System.out.println(p.getX());
- //FloatPoint使用
- Point<Float> p = new Point<Float>() ;
- p.setX(new Float(100.12f)) ;
- System.out.println(p.getX());

从结果中可以看到,我们实现了开篇中IntegerPoint类和FloatPoint类的效果。下面来看看泛型是怎么定义及使用的吧。
(1)、定义泛型:Point<T>
首先,大家可以看到Point<T>,即在类名后面加一个尖括号,括号里是一个大写字母。这里写的是T,其实这个字母可以是任何大写字母,大家这里先记着,可以是任何大写字母,意义是相同的。
(2)类中使用泛型
这个T表示派生自Object类的任何类,比如String,Integer,Double等等。这里要注意的是,T一定是派生于Object类的。为方便起见,大家可以在这里把T当成String,即String在类中怎么用,那T在类中就可以怎么用!所以下面的:定义变量,作为返回值,作为参数传入的定义就很容易理解了。
- //定义变量
- private T x ;
- //作为返回值
- public T getX(){
- return x ;
- }
- //作为参数
- public void setX(T x){
- this.x = x ;
- }
下面是泛型类的用法:
- //IntegerPoint使用
- Point<Integer> p = new Point<Integer>() ;
- p.setX(new Integer(100)) ;
- System.out.println(p.getX());
- //FloatPoint使用
- Point<Float> p = new Point<Float>() ;
- p.setX(new Float(100.12f)) ;
- System.out.println(p.getX());
- Point<String> p = new Point<String>() ;
而泛型类的构造则需要在类名后添加上<String>,即一对尖括号,中间写上要传入的类型。
因为我们构造时,是这样的:class Point<T>,所以在使用的时候也要在Point后加上类型来定义T代表的意义。
然后在getVar()和setVar()时就没有什么特殊的了,直接调用即可。
从上面的使用时,明显可以看出泛型的作用,在构造泛型类的实例的时候:
- //IntegerPoint使用
- Point<Integer> p = new Point<Integer>() ;
- //FloatPoint使用
- Point<Float> p = new Point<Float>() ;
前面我们提到ArrayList也是泛型,我们顺便它的实现:
- public class ArrayList<E>{
- …………
- }
(4)使用泛型实现的优势
相比我们开篇时使用Object的方式,有两个优点:
(1)、不用强制转换
- //使用Object作为返回值,要强制转换成指定类型
- Float floatX = (Float)floatPoint.getX();
- //使用泛型时,不用强制转换,直接出来就是String
- System.out.println(p.getVar());

可以看到,当我们构造时使用的是String,而在setVar时,传进去Integer类型时,就会报错。而不是像Object实现方式一样,在运行时才会报强制转换错误。
2、多泛型变量定义及字母规范
(1)、多泛型变量定义上在我们只定义了一个泛型变量T,那如果我们需要传进去多个泛型要怎么办呢?
只需要在类似下面这样就可以了:
- class MorePoint<T,U>{
- }
- class MorePoint<T,U,A,B,C>{
- }
- class MorePoint<T,U> {
- private T x;
- private T y;
- private U name;
- public void setX(T x) {
- this.x = x;
- }
- public T getX() {
- return this.x;
- }
- …………
- public void setName(U name){
- this.name = name;
- }
- public U getName() {
- return this.name;
- }
- }
- //使用
- MorePoint<Integer,String> morePoint = new MorePoint<Integer, String>();
- morePoint.setName("harvic");
- Log.d(TAG, "morPont.getName:" + morePoint.getName());
(2)、字母规范
在定义泛型类时,我们已经提到用于指定泛型的变量是一个大写字母:
- class Point<T>{
- …………
- }
- E — Element,常用在java Collection里,如:List<E>,Iterator<E>,Set<E>
- K,V — Key,Value,代表Map的键值对
- N — Number,数字
- T — Type,类型,如String,Integer等等
再重复一遍,使用哪个字母是没有特定意义的!只是为了提高可读性!!!!
3、泛型接口定义及使用
在接口上定义泛型与在类中定义泛型是一样的,代码如下:
- interface Info<T>{ // 在接口上定义泛型
- public T getVar() ; // 定义抽象方法,抽象方法的返回值就是泛型类型
- public void setVar(T x);
- }
与泛型类的定义一样,也是在接口名后加尖括号;
(1)、使用方法一:非泛型类
但是在使用的时候,就出现问题了,我们先看看下面这个使用方法:
- class InfoImpl implements Info<String>{ // 定义泛型接口的子类
- private String var ; // 定义属性
- public InfoImpl(String var){ // 通过构造方法设置属性内容
- this.setVar(var) ;
- }
- @Override
- public void setVar(String var){
- this.var = var ;
- }
- @Override
- public String getVar(){
- return this.var ;
- }
- }
- public class GenericsDemo24{
- public void main(String arsg[]){
- InfoImpl i = new InfoImpl("harvic");
- System.out.println(i.getVar()) ;
- }
- };
- class InfoImpl implements Info<String>{
- …………
- }
然后在在这里我们将Info<String>中的泛型变量T定义填充为了String类型。所以在重写时setVar()和getVar()时,IDE会也我们直接生成String类型的重写函数。
最后在使用时,没什么难度,传进去String类型的字符串来构造InfoImpl实例,然后调用它的函数即可。
- public class GenericsDemo24{
- public void main(String arsg[]){
- InfoImpl i = new InfoImpl("harvic");
- System.out.println(i.getVar()) ;
- }
- };
在方法一中,我们在类中直接把Info<T>接口给填充好了,但我们的类,是可以构造成泛型类的,那我们利用泛型类来构造填充泛型接口会是怎样呢?
- interface Info<T>{ // 在接口上定义泛型
- public T getVar() ; // 定义抽象方法,抽象方法的返回值就是泛型类型
- public void setVar(T var);
- }
- class InfoImpl<T> implements Info<T>{ // 定义泛型接口的子类
- private T var ; // 定义属性
- public InfoImpl(T var){ // 通过构造方法设置属性内容
- this.setVar(var) ;
- }
- public void setVar(T var){
- this.var = var ;
- }
- public T getVar(){
- return this.var ;
- }
- }
- public class GenericsDemo24{
- public static void main(String arsg[]){
- InfoImpl<String> i = new InfoImpl<String>("harvic");
- System.out.println(i.getVar()) ;
- }
- };
- class InfoImpl<T> implements Info<T>{ // 定义泛型接口的子类
- private T var ; // 定义属性
- public InfoImpl(T var){ // 通过构造方法设置属性内容
- this.setVar(var) ;
- }
- public void setVar(T var){
- this.var = var ;
- }
- public T getVar(){
- return this.var ;
- }
- }
然后在使用时,就是构造一个泛型类的实例的过程,使用过程也不变。
- public class GenericsDemo24{
- public static void main(String arsg[]){
- Info<String> i = new InfoImpl<String>("harvic");
- System.out.println(i.getVar()) ;
- }
- };
那我们稍微加深点难度,构造一个多个泛型变量的类,并继承自Info接口:
- class InfoImpl<T,K,U> implements Info<U>{ // 定义泛型接口的子类
- private U var ;
- private T x;
- private K y;
- public InfoImpl(U var){ // 通过构造方法设置属性内容
- this.setV