Java-泛型

Java泛型

java的泛型并非新的特性,在JDK1.5时就有的操做,其实咱们一直在使用泛型,常见的List,Map中都有它的身影java

那它有什么用处呢?以咱们最经常使用的ArrayList为例安全

public void test1() {
    ArrayList arrayList=new ArrayList();
    arrayList.add("张三");
    arrayList.add("李四");
    arrayList.add("王五");

    for (Object o : arrayList) {
        String name= (String) o;
        System.out.println(name);
    }
}

来看它的get方法返回类型ide

咱们都知道,List中能够存储数据,那么上面的代码看起来没有问题,运行也没有问题学习

可是,若是咱们往list添加个int类型的数据,那么在下面强制转换输出时,就会报出ClassCastException异常测试

缘由也很简单,咱们不能把一个int类型的变量强转为String类型,那么问题也很明显了,List的安全性idea

有解决方法吗,固然有,对于每一个类型单首创建对应的List,每一个List中只能存放对应的类型,这样虽然能解决问题,可是重复了大量相同代码,重复造轮子3d

因此在JDK1.5的时候,出现了一个新特性 : 泛型(genericity)code

程序不能肯定运行时的类型,可是使用Object来表示类型又太过笼统对象

好比北京大学旁边的理发店,理发店办活动,北京大学的学生来理发有优惠,那么理发店如何来指定那些人有优惠呢?blog

人类有优惠?范围太大.

具体的某我的有优惠?那么理发店把北京大学全部人名打印贴在墙上?这又太过于具体.

因此JDK想指定一些不肯定的类型,可是又不想太过于笼统,这就使用到了泛型

泛型本质是将类型参数化,经过传入的类型来判断当前运行的类型,而泛型又分为泛型类、泛型接口、泛型方法

咱们以常见的ArrayList为例:

public class ArrayList<E> ...{
     public boolean add(E e) {...}
     public E get(int index) {...}
}

咱们能够看到,在类名后面添加了一对尖括号<>里面有一个大写字母E 这就表示ArrayList为一个泛型类,括号中能够写多个泛型

在实例化时能够经过new ArrayList<>后面的尖括号中,来添加这个List中使用的类型

ArrayList<String> arrayList=new ArrayList<String>();

很明显,当咱们设置完List的使用类型后,咱们add Int类型的方法出现了异常

idea提示咱们所需的类型是一个String类型,而咱们提供的是一个int类型,这也说明了泛型具备检查的类型的功能

在JDK1.7以后,若是接收类型中显式设置了类型,那么实例化对象能够不用写具体的类型,只须要写一对尖括号便可

ArrayList<String> arrayList=new ArrayList<>();

那么设置完泛型后,咱们在来看get方法的返回类型

这里能够看到,在咱们设置完List的泛型后,返回的类型已经为具体的String类了

那么它是如何实现动态的类型变化的呢?

咱们来本身建立一个泛型类,一切就都知道了

医院类

public class Hospital<T> {
    
    /**
     * 输出动物的信息方法
     * @param t
     */
    public void print(T t){
	System.out.println(t);
    }
    
    /**
     * 给宠物打疫苗方法
     * @return T
     */
    public T vaccine(T t){
        System.out.println("给宠物打疫苗");
        return t;
    }
}

猫类

public class Cat {
    
    private int age;
    private String name;
    
    //省略构造,get,set,toString方法
}

固然,上面这个例子可能不是太好,这种状况彻底能够抽出父类Animal类进行多态的实现,可是咱们如今仅仅为了学习泛型

那么上面医院类Hospital的< T > 以及下面两个方法中的T是什么意思呢?

咱们能够想象为,当实例化Hospital医院类时候,经过new Hospital< Cat>(); 将猫Cat类传入到Hospital类中,那么在这个已经实例化的Hospital对象中,全部的T都表明为传入的Cat类,简单点说就是,传入啥类型T就是啥类型

那么T是java的关键字吗,并非,咱们能够任意起名,就像和给变量起名同样,可是泛型有些规范咱们要尽可能遵照

java泛型字母表明意思

E Element (在集合中使用,由于集合中存放的是元素)
T Type(Java 类)
K Key(键)
V Value(值)
N Number(数值类型)
public void test1() {
    Hospital<Cat> catHospital = new Hospital<>();
    catHospital.print(new Cat(5,"小花"));
}

咱们能够看到,当设置完泛型后,咱们能够动态实现对于一个类的不一样实现

继承关系

声明泛型的类和普通的类没有太大的区别,一样可使用继承

可能会出现子类保留,指定,新增泛型的状况

对于上面这几种状况直接概况一下

  • 若是子类没有保留父类泛型,那么父类泛型默认为Object
  • 若是指定父类泛型的类型,那么父类泛型类型为指定的类型
  • 若是子类保留了父类类型,那么泛型类型就为子类实例化时设置的类型

什么意思呢?咱们来看

这种状况在实例化Son对象时,设置AB的泛型类型,那么Son中和Father中的类型就会是设置的类型

好比设置为String,Integer,那么子类和父类中的泛型类型都为String和Integer

//父类
public class Father<A,B> {
}
//子类
public class Son<A,B> extends Father<A,B>{
    public void print(A a,B b){
        System.out.println("Son-A的类型:"+a.getClass());
        System.out.println("Son-B的类型:"+b.getClass());
    }
}
//测试
public void test1() {
    Son<String, Integer> son = new Son<>();
    son.print("",1);
}
//结果
Son-A的类型:class java.lang.String
Son-B的类型:class java.lang.Integer

除了子类保留父类,还能够直接指定父类泛型类型

//父类
public class Father<A,B> {
}
//子类        在这里直接设置了父类泛型类型  ↓
public class Son<A,B> extends Father<Integer,B>{
    public void print(A a,B b){
        System.out.println("Son-A的类型:"+a.getClass());
        System.out.println("Son-B的类型:"+b.getClass());
    }
}

那么在实例化子类时即便设置new Son<String,String>(); 那么父类的两个泛型类型只会是Integer和String,由于泛型A已经在子类中指定了

还有一种没有保留父类泛型类型

//不保留也不指定父类泛型类型            ↓ 去掉这里的 <A,B>
public class Son<A,B> extends Father{
    public void print(A a,B b){
        System.out.println("Son-A的类型:"+a.getClass());
        System.out.println("Son-B的类型:"+b.getClass());
    }
}

若是不保留父类泛型类型,也不指定父类泛型类型,那么泛型类型默认为Object至关于extends Father<Object,Object>

若是部分保留,除非子类须要的话,那么子类只须要声明保留的泛型便可

泛型接口 和泛型类继承一致,这里再也不赘述

泛型方法

泛型方法不必定存在泛型类或泛型接口中,只对方法而言,传入的参数类型不肯定

例以下面这个getMax方法,若是是Integer返回最大值,若是是String根据ASII码返回最大值,假设传入List中的类型为String或Integer

public <E> E getMax(List<E> es){
    if(es==null || es.size()==0)
        return null;
    E e = es.get(0);
    if (e instanceof Integer) {
        //获取最大值操做...
    }
    if(e instanceof String){
        //获取最大值操做...
    }
    return null;
}

咱们能够看到,传入的参数并不肯定,这种方法能够称之为泛型方法,泛型方法怎么声明呢?

//在返回类型前面加上<X> X能够换成任何字母,可是尽可能符合规范
//那么在返回类型,传入参数类型,方法体中均可以使用这个定义泛型
public <E> List<E> get(E[] arrays){
 
}

?通配符

?表示未知类型,

例如List<?> 那么任何类型的List均可以赋值给这个List,可是注意,并不能直接往这个List中添加数据

也有个例外,添加null能够

public void test1() {
    List<?> list=new ArrayList<>();
    //    list.add("小明"); 报错 须要的类型为 ?    而咱们传入类型为String
    //可是下面这些赋值都没有问题
    List<String> stringList = new ArrayList<>();
    List<Integer> integerList = new ArrayList<>();
    list = stringList;
    list = integerList;
}

List<?> 的get方法,获取的类型是Object

通配符集合能够做为方法的形参,来接收List集合中不肯定的类型,例以下面这个例子

//这里使用迭代器来进行遍历
public void print(List<?> list){
    ListIterator<?> listIterator = list.listIterator();
    while (listIterator.hasNext()){
        Object next = listIterator.next();
        System.out.println(next);
    }
}

这里有须要注意的点,例如X类为Y类的父类,泛型genericity 简称G G< Y >并不能直接赋值给G< X >,例以下面这个例子

String为Object的子类,为何不能赋值给Object的集合呢?

这里咱们须要将它们想象成两个类,虽然在运行中只是一个类,咱们能够想象为一个类只为Object提供服务,另外一个只为String提供服务,虽然Object和String有继承关系,可是它们两个集合并无任何关系,只是功能相同而已并不存在List< String>继承List< Object>的关系

就像请俩技师一个给儿子捏脚一个给爸爸捏脚,虽然儿子和爸爸有继承关系,可是两个技师没有任何关系(假设)

通配符限制条件

咱们前面看到了使用?通配符能够传入任何类型,可是使用Object做为形参同样能够完成,因此为了安全性,通配符还提供了一系列限制条件

<? extends xxx><? super xxx>

先来讲<? extends xxx> 能够匹配继承于xxx或者xxx类型 小于等于该类的类型

咱们使用经常使用的List来测试,建立3个类,类中什么都没有,仅是来测试通配符

Animal动物类为父类->Cat猫类继承Animal类-> XiaoHuaCat小花猫类继承Cat类

@Test
public void test1() {
    //设置通配符限制条件
    List<? extends Cat> list = new ArrayList<>();
    List<Cat> cats = new ArrayList<>();
    List<XiaoHuaCat> xiaoHuaCats = new ArrayList<>();
    List<Animal> animals = new ArrayList<>();

    list = cats;
    list = xiaoHuaCats;
    //这条赋值语句报错,由于不是继承Cat或Cat类型
    list = animals;
}

那么它get方法返回的类型为Cat类,由于这已是在这个集合中最大的父类了,它在这个集合中不可能再有父类了

再来讲<? extends xxx> 能够匹配xxx继承的类类型或者xxx类型 大于等于该类的类型

@Test
public void test1() {
    List<? super Cat> list = new ArrayList<>();
    List<Cat> cats = new ArrayList<>();
    List<Animal> animals = new ArrayList<>();
    List<XiaoHuaCat> xiaoHuaCats = new ArrayList<>();

    list = cats;
    list = animals;
    //这条语句会报错,由于最小的类型就是Cat类了,而XiaoHuaCat是继承于Cat的
    list = xiaoHuaCats;
}

它的get方法返回类型为Object ,由于不管哪一个类,它的祖先类必定是Obejct类,由于父类对象引用子类对象是容许的,因此get的是全部类的父类

本文仅我的理解,若是有不对的地方欢迎评论指出或私信,谢谢٩(๑>◡<๑)۶

相关文章
相关标签/搜索