zoukankan      html  css  js  c++  java
  • 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>();
    • 总结
      • 限定通配符总是包括自己
  • 相关阅读:
    leetcode701. Insert into a Binary Search Tree
    leetcode 958. Check Completeness of a Binary Tree 判断是否是完全二叉树 、222. Count Complete Tree Nodes
    leetcode 110. Balanced Binary Tree
    leetcode 104. Maximum Depth of Binary Tree 111. Minimum Depth of Binary Tree
    二叉树
    leetcode 124. Binary Tree Maximum Path Sum 、543. Diameter of Binary Tree(直径)
    5. Longest Palindromic Substring
    128. Longest Consecutive Sequence
    Mac OS下Android Studio的Java not found问题,androidfound
    安卓 AsyncHttpClient
  • 原文地址:https://www.cnblogs.com/homeword/p/7497121.html
Copyright © 2011-2022 走看看