Java | 泛型

泛型

什么是泛型?

泛型,即‘参数化类型’,顾名思义,就是将类型由原来的具体的类型参数化,类似于方法中的变量参数,此时类型也定义成参数形式(可以称之为类型形参),然后在使用/调用时传入具体的类型(类型实参)。

为什么要使用泛型?

先看一个例子:

    List list = new ArrayList();
    list.add("abc");
    list.add(1);

    for (Object o : list) {
        String str = (String)o;
        System.out.println(str.length());
    }

在这个小Demo运行的结果:java.lang.ClassCastException: java.lang.Integer cannot be cast to java.lang.String
现以这样的结果不是我们想要的,但是如何避免这样的错误呢?


所以这个时候,就要使用泛型了:

    List<String> list = new ArrayList<>();
    list.add("abc");
    //list.add(1);  //报错,不能添加非字符串类型
    list.add("efg");

我们在声明的时候,加上泛型,是一个字符串类型,所以如果使用这个集合添加别的类型的值,他就会报错,并且还是在编译阶段就报错。


所以使用泛型可以有效有避免在编译中所遇到的错误,可以快速的发现错误并修改。

泛型的特性

下面有一个例子,在家猜一下答案:

    List<String> stringList = new ArrayList<>();
    List<Integer> integerList = new ArrayList<>();
    System.out.println(stringList.equals(integerList));

上面这一段代码中,定义了两个集合,然后集合中都没有存储数据,但是他们的泛型是不一样的,最后比较一下,他们的结果:true


这就是泛型的特性,泛型的擦除 ,泛型只在译编阶段有效果,在编译的时候,只要编译正确就会将泛型的相关信息给擦除了,所以说泛型的信息不会进入到运行阶段。


当然,有的时候,定义泛型之后,还需要存储别的类型,这个时候,可以使用反射来往容器里面存数据。

泛型的分类

泛型分为三种:泛型类泛型接口泛型方法
泛型常见的字母:

T Type 表示类型。
K 代表键值对中的Key
V 代表键值对中的Value
E 代表Element
?表示不确定类型

泛型类

定义一个泛型类:

    public class Student<T> {
        T id;

        public T getId(T id) {
            return id;
        }

        public void setId(T id) {
            this.id = id;
        }
    }

使用:

    Student<Integer> stu = new Student<>();
    stu.setId(1);
    Integer id = stu.getId();
    System.out.println(id);    //1

    Student<String> stuStr = new Student<>();
    stuStr.setId("10001");
    String strid = stuStr.getId();
    System.out.println(strid);  //10001
    
    Student stuStr = new Student);
    stuStr.setId("2222");
    Object objid  = stuStr.getId();
    System.out.println(objid);  //2222

在上面的例子中,我们可以看到,使用不同的泛型声明对象,存进去和取出来的id类型是不是一样的,并且,如果没有定义泛型,默认的就是Object类型。就是一种泛型的擦除。如果定义的话,那么所有的Student类里面的<T>都会替换成声明进修的类型。

泛型类的继承

在泛型的继承里面,重写的方法都是由父类的泛型所决定的,如果父类没有决定泛型,那么子类也不可以指定泛型,如果父类和子类都没有指定泛型,那么<T>就会由Object来代替。在子类里面定义属性的时候,是根据子类的泛型的。
父类指定泛型

    class Test extends Student<String>{
        @Override
        public String getId() {
            return super.getId();
        }

        @Override
        public void setId(String id) {
            super.setId(id);
        }
    }

父类子类都没有泛型

    class Test1 extends Student{

        @Override
        public Object getId() {
            return super.getId();
        }

        @Override
        public void setId(Object id) {
            super.setId(id);
        }
    }

父类子类都有泛型

    class Test2<T> extends Student<T> {

        @Override
        public T getId() {
            return super.getId();
        }

        @Override
        public void setId(T id) {
            super.setId(id);
        }
    }

只有子类有泛型

    class Test2<T> extends Student {

        @Override
        public Object getId() {
            return super.getId();
        }

        @Override
        public void setId(Object id) {
            super.setId(id);
        }
    }

泛型接口

泛型的接口使用的时候,和泛型的类的定义是一个的,不管现实还是继承都和泛型类一样。

public interface GenericInterface<T> {}

泛型方法

泛型方法的格式:

修饰符 <代表泛型的变量> 返回值类型 方法名(参数列表){}

含有泛型的方法,在调用方法的时候确定泛型的数据类型,传递什么类型的参数,泛型就是什么类型的。
定义一个泛型方法并使用:

    public static void main(String[] args) {
        Demo01 demo01 = new Demo01();
        demo01.test("a");   //a
        demo01.test(2);     //2
        demo01.test(false); //false
        demo01.test(0.01);  //0.01

        Demo01.staticTest(4);   ///4
    }

    /*
        普通泛型方法
     */
    public <T> void test(T t) {
        System.out.println(t);
    }

    /*
        静态泛型方法
     */
    public static <T> void staticTest(T t) {
        System.out.println(t);
    }

泛型通配符

当我们不知道要使用的具体类型的时候,我们就可以使用<?>来表示泛型。
使用:

    public static void main(String[] args) {
        //可以声明的时候使用,但是不可以定义的时候使用
        List<?> list = new ArrayList<String>();
        //定义的时候,必须要指定具体的类型
        //List<?> list1 = new ArrayList<?>();
        List<String> strings = new ArrayList<>();
        test(strings);
        //<?>通配符可以接收任何类型
        List<Integer> integerList = new ArrayList<>();
        test(integerList);
    }

    public static void test(List<?> list) {}

在泛型里面不存在继承的关系,如:

    public static void main(String[] args) {
        List<String> strings = new ArrayList<>();
        //test(strings); //报错
    }

    public static void test(List<Object> list) {}

不可以用Object类型的集合来接收String类型的集合,但是有的时候,我们必须要用这一种方法,那么我们就可以给泛型设置上限和下限来完成这种操作。

泛型的上限

格式: 类型名称 <? extends 类 > 对象名称
意义: 只能接收该类型及其子类
泛型的下限
格式: 类型名称 <? super 类 > 对象名称
意义: 只能接收该类型及其父类型


比如:现已知Object类,String 类,Number类,Integer类,其中Number是Integer的父类。

    public static void main(String[] args) {
        Collection<Integer> list1 = new ArrayList<Integer>();
        Collection<String> list2 = new ArrayList<String>();
        Collection<Number> list3 = new ArrayList<Number>();
        Collection<Object> list4 = new ArrayList<Object>();

        getElement(list1);
        getElement(list2);//报错
        getElement(list3);
        getElement(list4);//报错

        getElement2(list1);//报错
        getElement2(list2);//报错
        getElement2(list3);
        getElement2(list4);

    }
    // 泛型的上限:此时的泛型?,必须是Number类型或者Number类型的子类
    public static void getElement1(Collection<? extends Number> coll){}
    // 泛型的下限:此时的泛型?,必须是Number类型或者Number类型的父类
    public static void getElement2(Collection<? super Number> coll){}


关注公众号,随时获取最新资讯

细节决定成败!
个人愚见,如有不对,恳请斧正!

posted @ 2020-04-15 21:47  一点浩然气~  阅读(270)  评论(0编辑  收藏  举报