• JAVA基础_泛型


    什么是泛型


    泛型是提供给javac编译器使用的,可以限定集合中的输入类型,让编译器挡住源程序中的非法输入,编译器编译带类型说明的集合时会去除掉”类型”信息,是程序的运行效率不受影响,对于参数化的泛型类型,getClass()方法返回值和原始类型完全一样。由于编译生成的字节码会擦除泛型的类型信息,只要能跳过编译器,就可以往某个泛型集合中加入其它类型的数据,例如,用反射得到集合,然后再调用add()方法即可


    GenericDemo.java

    public class GenericDemo {
        
        public static void main(String[] args) {
            
            // 未使用泛型,则可以添加任意Object类型的数据
            List noGrnerics = new ArrayList<>();
            noGrnerics.add(1);
            noGrnerics.add("hello");
            noGrnerics.add("1L");
            noGrnerics.add(new GenericDomain(1L,"Generic"));
            // 由于没有使用泛型,在取出数据时必须要知道某个位置上的某个数据的数据类型,并加以强制转换,否则要出错,除非用Object接收。
            Integer integer = (Integer) noGrnerics.get(0);
            System.out.println("integer = " + integer);
            // 由于不知道某个位置上的数据类型,用Object接收
            Object objIndex1 = noGrnerics.get(1);
            System.out.println("objIndex1 = " + objIndex1);
            Object objeIndex3 = noGrnerics.get(3);
            System.out.println("objeIndex3 = " + objeIndex3);
            // 虽然可以通过类对象来知道类属性,但是这样不是太麻烦了吗??
            System.out.println("objeIndex3.getClass() = " + objeIndex3.getClass());
        
            
            // 使用泛型
            List<String> strs = new ArrayList<>();
            strs.add("str1");
            strs.add("str2");
            strs.add("str3");
            // 因为指定了这个List中只能放置String类型的数据,当这里放置其他类型的数据时就会报错,因为不能装进去
            // 从某种角度上可以认为泛型解决了强制类型转换,使数据特征更明显,拥有一致性
    //        strs.add(1);
            String str = strs.get(0);
            System.out.println("str = " + str);
            
        }
    
    }

    绕过编译时泛型,添加非泛型类型数据:GenericDemo2.java

    public class GenericDemo2 {
    
        /**
         * 程序目标,向一个List<String>的泛型数组中添加Integer类型的数据
         * 
         * @param args
         */
        public static void main(String[] args) {
    
            List<String> strs = new ArrayList<>();
            strs.add("str1");
            strs.add("str2");
            strs.add("str3");
            strs.add("str4");
            // 这里是添加不进去的,因为指定了泛型类型,且没有泛型擦除
            // strs.add(new Integer(1));
            System.out.println("Before add a Integer value : " + strs);
    
            // 通过泛型在运行时擦除的原理,利用反射向泛型中添加Integer数据
            Class<List<String>> clazz = (Class<List<String>>) strs.getClass();
            try {
                // 这里在利用反射获取add方法时,如果参数类型指定为String.class,
                // 则说明add方法为,java.util.ArrayList.add(java.lang.String)
                // 而我们传入了一个Integer类型的数据,则显然不能添加成功,会抛出 java.lang.NoSuchMethodException
                clazz.getDeclaredMethod("add", String.class).invoke(strs, new Integer(1));
                // 这里原因一样,java.util.ArrayList.add(java.lang.Integer) 但是用的int,
                // 请记住,java是强类型语言,这里不存在自动装箱和拆箱
                clazz.getDeclaredMethod("add", Integer.class).invoke(strs, 2);
                // 这里也是不正确的,因为Class<List<String>>指定了放String
                clazz.getDeclaredMethod("add", Integer.class).invoke(strs, new Integer(4));
                //同上
                clazz.getDeclaredMethod("add", int.class).invoke(strs, 5);
            } catch (Exception e) {
                e.printStackTrace();
            } finally {
                //正确做法
                try {
                    clazz.getDeclaredMethod("add", Object.class).invoke(strs, 3);
                    System.out.println("strs = " + strs);
                    // 只有Object能行
                    clazz.getDeclaredMethod("add", String.class).invoke(strs, "4");
                    System.out.println("strs = " + strs);
                } catch (IllegalAccessException | IllegalArgumentException | InvocationTargetException
                        | NoSuchMethodException | SecurityException e) {
                    // TODO Auto-generated catch block
                    e.printStackTrace();
                }
            }
            System.out.println("After add a Integer value : " + strs);
        }
    
        
    }

    解释为什么只能用Object,运行时会擦除泛型,下面的代码清单就可以验证

    public class GenericTypeDemo {
    
        public static void main(String[] args) {
            // 这里的字节码类型为ArrayList
            List<String> strings = new ArrayList<>();
            // 这里的字节码类型也为ArrayList
            List<Integer> integers = new ArrayList<>();
            System.out.println("strings.getClass() = " + strings.getClass());
            System.out.println("integers.getClass() = " + integers.getClass());
            System.out.println("integers.getClass() == strings.getClass() = " 
            + (integers.getClass() == strings.getClass()));
        }
        
    }


    泛型中的?通配符

    • 需求:定义一个方法,该方法用于打印出任意参数类型化的集合中的所有数据。


    public class GenericDemo3 {
        
        
        public static void main(String[] args) {
            
            // 需求:定义一个方法,该方法用于打印出任意参数类型化的集合中的所有数据。
            List<String> strings = new ArrayList<>();
            strings.add("a");
            strings.add("b");
            strings.add("c");
            strings.add("f");        
            printCollectionElements(strings);
            printStringCollectionElements(strings);
            List<Integer> integers = new ArrayList<>();
            integers.add(1);
            integers.add(2);
            integers.add(3);
            integers.add(4);
            printCollectionElements(integers);
            // 这里就不能用了
    //        printStringCollectionElements(integers);
            
        }
    
        /**
         * 定义一个方法,该方法用于打印出任意参数类型化的集合中的所有数据。
         * 这种是通配方法,能够打印所有的,其通配之处就在与?,?可以引用其他各种参数化的类型
         * @param collection
         */
        public static void printCollectionElements(Collection<?> collection) {
            collection.forEach(System.out::println);
        }
        
        /**
         * 打印String类型的集合数据,由于制定了泛型类型为String,则只能打印String类型的集合
         * @param collection
         */
        public static void printStringCollectionElements(Collection<String> collection) {
            collection.forEach(System.out::println);
        }
    
    }


    • 总结

    使用?通配符可以引用其他各种参数化的类型,?通配符定义的变量主要用作引用,可以调用与参数化无关的方法,如size(),不能调用与参数化有关的方法,如add(E e)


    泛型中?的扩展

    • 限定通配符的上边界
      • 正确 List<? extends Number> x = new ArrayList<Integer>();
      • 错误 List<? extends Number> x = new ArrayList<String>();
    • 限定通配符的下边界
      • 正确 Vector<? super Integer> x = new Vector<Number>();
      • 错误 Vector<? super Integer> x = new Vector<Byte>();
    • 总结
      • 限定通配符总是包括自己
  • 相关阅读:
    Spring boot mvn
    软考
    java
    webserver代理生成本地类的两种方式
    行转列语句,记录一下
    React.PureComponent浅比较理解
    关于职业规划和职场规则以及未来发展发方向
    程序员的一天
    代码commit规范
    element UI 使用总结
  • 原文地址:https://www.cnblogs.com/homeword/p/7497121.html
Copyright © 2020-2023  润新知