• Java范型学习笔记


    对于范型的使用或者说印象只有集合,其他地方即使使用过也不知道,反正就是只停留在List<E> Map<K, V>,最近刚好闲来无事,就找找资料学习一下;下列为个人学习总结,欢迎学习交流;

    1. 什么是java泛型

    范型:参数化类型,所操作的数据类型被指定为一个参数。这种参数类型可以用在类、接口和方法的创建中,分别称为泛型类、泛型接口、泛型方法;

    List<Integer> list = new ArrayList<>();
    

    上述代码申明了一个集合,操作的数据类型被指定为Integer(此处Integer为类型参数);

    2. 为什么需要泛型

    引入泛型的好处是可以将运行时错误提前到编译时错误

    List list = new ArrayList();
    list.add(100);
    list.add("zhangsan");
    
    for(int i = 0; i< list.size();i++){
        int num = (int)list.get(i);
    }
    

    上面的代码在编译时没有任何问题,但是在运行时会报错:

    java.lang.ClassCastException: java.lang.String cannot be cast to java.lang.Integer
    

    如果没有引入范型,集合内的操作数据类型可以是任何类型,如果在操作数据时进行类型判断然后在强转也是没有问题的,但是很明显不切合实际,所以如果引入范型对操作数据类型做一定的约束的话,将会对后续的操作提供太多的方便也能减少错误的出现;

    List<Integer> list = new arrayList<>();
    //list.add("zhangsan");//在编译期就会报错
    

    3. 范型的使用

    泛型有三种使用方式,分别为:泛型类、泛型接口、泛型方法

    3.1. 范型类

    泛型类中的类型参数几乎可以用于任何可以使用接口名、类名的地方

    /**
     * 范型标识可以是任何标识符号,如常见的E, T, K, V ...
     */
    class 类名<范型标识>{
    }
    
    class Stu<T>{
    }
    
    class People<E>{
    }
    

    注意:

    • 泛型的类型参数只能是类类型,不能是基础类型;
    List<int> list;//基础类型不能当作类型参数    
    
    • 不能对确切的泛型类型使用instanceof操作。如下面的操作是非法的,编译时会出错。
    if(item instanceof List<Integer>) // Illegal generic type of instanceof
    

    3.2. 范型接口

    泛型接口与泛型类的定义及使用基本相同

    public interface Iterable<T> {
        Iterator<T> iterator();
    }
    
    1. 实现范型接口,未明确范型时:实现类后的范型标识不能省略
    class Iter<T> implements Iterable<T> {
        @Override
        public Iterator<T> iterator() {
            return null;
        }
    }
    
    1. 实现范型接口,明确范型时:实现类后的范型标识省略
    class Iter1 implements Iterable<String> {
        @Override
        public Iterator<String> iterator() {
            return null;
        }
    }
    

    3.3. 范型方法

    public 与 返回值 中间非常重要,声明此方法为泛型方法;

    /**
     * 泛型方法的基本介绍
     * @param tClass 传入的泛型实参
     * @return T 返回值为T类型
     * 说明:
     *     1)public 与 返回值 中间<T>非常重要,声明此方法为泛型方法;
     *     2)只有声明了<T>的方法才是泛型方法,若没有<T>泛型类中即使使用了泛型的成员方法也不是泛型方法;
     *     3)<T>表明该方法将使用泛型类型T,此时才可以在方法中使用泛型类型T;
     *     4)与泛型类的定义一样,此处T可以随便写为任意标识,常见的如T、E、K、V等形式的参数常用于表示泛型;
     */
    public <T> T genInstance(Class<T> tClass) throws IllegalAccessException, InstantiationException {
        T instance = tClass.newInstance();
        return instance;
    }  
    

    光看上面的例子可能依然会非常迷糊,我们再通过一个例子

    public class ArrayList<E> implements List<E> {
    
        /**
         * 虽然在方法中使用了泛型,但是这并不是一个泛型方法。
         * 这只是类中一个普通的成员方法,只不过他的返回值是在声明泛型类已经声明过的泛型。
         * 所以在这个方法中才可以继续使用 T 这个泛型。
         */
        public E get(int index) {
            //...
            return elementData(index);
        }
    
        /**
         * 将E改为T后,方法报错,"cannot reslove symbol T"
         * 因为在类的声明中并未声明泛型T,所以在使用E做形参和返回值类型时,编译器会无法识别
         */
        public T set(int index, T element) {
            //...
            return oldValue;
        }
    
        /**
         * 这才是一个真正的泛型方法。
         * 首先在public与返回值之间的<T>必不可少,这表明这是一个泛型方法,并且声明了一个泛型T
         * 这个T可以出现在这个泛型方法的任意位置.
         */
        public <T> T[] toArray(T[] a) {
            //...
            return a;
        }
    }    
    

    4. 范型通配符

    使用?代替范型标识,标识操作数据类型可以为任何数据类型

    java中我们都知道父类可以出现的地方,子类都是可以出现的,但是:

    public static void method(List<Number> list){
    }
    
    public static void main(String[] args) {
        List<Integer> list = new ArrayList<>();
        method(list); // 此处报错:List<java.lang.Number> cannot be applied to List<java.lang.Integer>
    }
    

    通过提示信息我们可以看到List<Integer>不能被看作为List<Number>的子类。
    由此可以看出:同一种泛型可以对应多个版本(因为参数类型是不确定的),不同版本的泛型类实例是不兼容的。
    我们可以将上面的方法改一下:

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

    类型通配符一般是使用?代替具体的类型实参,此处的和Number、String、Integer一样都是一种实际的类型,可以把看成所有类型的父类,是一种真实的类型;
    可以解决当具体类型不确定的时候,这个通配符就是?;当不需要使用类型的具体功能只使用Object类中的功能,那么可以用 ? 通配符来表未知类型。

    5. 泛型上下边界

    在使用泛型的时候,我们还可以为传入的泛型类型实参进行上下边界的限制,如:类型实参只准传入某种类型的父类或某种类型的子类。

    public static void method(List<? extends Number> list){
    }
    
    public static void main(String[] args) {
        List<Integer> intList = new ArrayList<>();
        method(intList); // Integer 是Number的子类
    
        List<Double> doubleList = new ArrayList<>();
        method(doubleList); // Double是Number的子类
    
        List<String> strList = new ArrayList<>();
        method(strList); // 此处报错,String不是Number的子类
    }
    

    6. 范型擦除

    Java在编译期间,所有的泛型信息都会被擦掉;
    如在代码中定义List<Object>List<String>等类型,在编译后都会变成List,JVM看到的只是List,而由泛型附加的类型信息对JVM是看不到的。Java编译器会在编译时尽可能的发现可能出错的地方,但是仍然无法预知在运行时刻出现的类型转换异常的情况;

    擦除范型后只保留原始类型

    原始类型 就是擦除去了泛型信息,最后在字节码中的类型变量的真正类型,无论何时定义一个泛型,相应的原始类型都会被自动提供,类型变量擦除,并使用其限定类型(无限定的变量用Object)替换。

    List<String> list1 = new ArrayList<>();
    list1.add("abc");
    
    List<Integer> list2 = new ArrayList<>();
    list2.add(123);
    
    System.out.println(list1.getClass() == list2.getClass()); // true
    System.out.println(list1.getClass()); // class java.util.ArrayList
    

    说明泛型类型String和Integer都被擦除掉了,只剩下原始类型List;

    ArrayList<Integer> list = new ArrayList<Integer>();
    
    list.add(1);  //这样调用 add 方法只能存储整形,因为泛型类型的实例为 Integer
    list.add("asd"); // 此处报错
    
    list.getClass().getMethod("add", Object.class).invoke(list, "asd"); // 通过反射获取实例后可以添加成功
    
    for (int i = 0; i < list.size(); i++) {
        System.out.println(list.get(i));
    }
    

    在程序中定义了一个ArrayList泛型类型实例化为Integer对象,如果直接调用add()方法,那么只能存储整数数据,不过当我们利用反射调用add()方法的时候,却可以存储字符串,这说明了Integer泛型实例在编译之后被擦除掉了,只保留了原始类型。

    参考资料:

    https://www.cnblogs.com/wuqinglong/p/9456193.html

    https://blog.csdn.net/caihuangshi/article/details/51278793

    https://www.cnblogs.com/iyangyuan/archive/2013/04/09/3011274.html

    https://blog.csdn.net/caihuangshi/article/details/51278793

  • 相关阅读:
    【python】UI自动化框架搭建.优化
    【fiddler】mock测试
    【postman】对于postman简单的理解
    【pycharm】报错:windows找不到文件chrome
    【python】UI自动化操作属于div标签的滚动条滚动
    【fiddler】fiddler监听local host和127.0.0.1
    【PLSQL】PLSQL过期解决方案(注册机或者修改注册表)
    【奇怪的知识四】:一些常用的下载网站地址
    【奇怪的知识三】:一个可以变色的心形.bat
    python中request获取json数据
  • 原文地址:https://www.cnblogs.com/dafengdeai/p/12345632.html
Copyright © 2020-2023  润新知