美文网首页
Java - 泛型数组

Java - 泛型数组

作者: xiaofudeng | 来源:发表于2017-11-25 20:31 被阅读0次

    Java不允许创建泛型数组的原因

    上面链接中的Xuan Luo的回答很好. 简单来说就是Java数组必须有一个具体的类型信息, 但是因为泛型擦除, 所以无法提供具体的类型信息给数组, 因而无法创建泛型数组. 而容器其实泛型擦除后根本没类型信息了, 所以全部的List<AnyType>最后都是List. 看下面这个例子:

        public static void testTypeInformation(){
            String[] strings1 = new String[0];
            String[] strings2 = new String[0];
            Integer[] integers = new Integer[0];
            System.out.println(String.format("strings1.class == strings2.class: %s", strings1.getClass() == strings2.getClass()));
            // 直接这样写编译器直接给出了错误
            // strings1.getClass() == integers.getClass()
            // System.out.println(String.format("strings1.class == integers.class: %s", strings1.getClass() == integers.getClass()));
            Class<?> stringArrayClass = strings1.getClass();
            Class<?> integerArrayClass = integers.getClass();
            System.out.println(String.format("strings1.class == integers.class: %s", stringArrayClass == integerArrayClass));
    
            // 再看容器类
            List<String> stringList = new ArrayList<>();
            List<Integer> integerList = new ArrayList<>();
            System.out.println(String.format("stringList.class == integerList.class: %s", stringList.getClass() == integerList.getClass()));
    
        }
    

    输出:

    strings1.class == strings2.class: true
    strings1.class == integers.class: false
    stringList.class == integerList.class: true
    

    可以看到数组是确确实实有具体类型, 而泛型容器最终都是容器class本身, 没有新的类生成.

    一些创建泛型数组的方法

    调用反射包的方法

        /**
         * 该方法可以安全地创建泛型数组
         * 调用反射包里的Array.newInstance(type, length)方法.
         * 具体的实现是靠native方法.
         * @param type 元素类型
         * @param length 长度
         * @param <T> 类型
         * @return
         */
        @SuppressWarnings("unchecked")
        public static <T> T[] createArray(Class<T> type, int length){
            return (T[]) Array.newInstance(type, length);
        }
    

    强制类型转换 (错误方法)

        /**
         * 编译不会报错, 但是运行时
         * 只要T不是Object, 那一定会报错
         * 因为Java对于数组的定义, 是必须要有具体类型的
         * 虽然 String 可以转换为 Object, 但是Object不一定能转换成String
         * 一样的道理 String[] 可以转换为 Object[], 但是Object[]不一定能
         * 转换成String[], 另外String[]转换为Object[]是合法的, 但是千万
         * 不要这样做, 很可能会出错, 因为转换为Object[]就能存任意引用了,
         * 但是底层仍然是String[], 如果存的不是String, 那么就会报错.
         * @param len 长度
         * @param <T> 类型参数, 可由类型推导推导得知, 也可以显式
         *           指定, ClassName.<Type>function()
         * @return
         */
        @SuppressWarnings("unchecked")
        public static <T> T[] createArray(int len){
            return (T[]) new Object[len];
        }
    

    测试:

            // ClassCastException
            // Exception in thread "main" java.lang.ClassCastException: [Ljava.lang.Object; cannot be cast to [Ljava.lang.Integer;
            // 使用了类型推导
            try {
                Integer[] integers = createArray(10);
            } catch (ClassCastException e){
                System.out.println("exception: " + e.getMessage());
            }
    
            // 也可以这样, 显式指定类型
            // Integer[] integerArray = GenericArray.<Integer>createArray(10);
    

    因为数组的实际类型是Object[], 是无法转型为Integer[]的.

    限制访问的Object[]方法

        /**
         * 使用底层Object[]来存储元素的折中做法
         * 不能暴露Object[]出去, 否则无法保证
         * 往里面加的类型是否安全
         * @param <T>
         */
        public static class MyArray<T>{
    
            // 存放实际的数据
            // 因为所有对象都能向上转型为Object
            // 所有用Object[]来存各类对象是没问题的
            private Object[] innerArray;
    
            public MyArray(int len){
                innerArray = new Object[len];
            }
    
            @SuppressWarnings("unchecked")
            public T get(int index){
                // 只要set方法中传入的确实是T类型的对象
                // 那么这里一定是安全的
                return (T) innerArray[index];
            }
    
            public void set(int index, T item){
                innerArray[index] = item;
            }
    
            @Override
            public String toString() {
                return Arrays.toString(innerArray);
            }
        }
    

    测试:

            MyArray<String> stringArray = new MyArray<>(3);
            stringArray.set(0, "The");
            stringArray.set(1, "safe");
            stringArray.set(2, "generic array");
            System.out.println(stringArray);
    

    输出:

    [The, safe, generic array]
    

    相关文章

      网友评论

          本文标题:Java - 泛型数组

          本文链接:https://www.haomeiwen.com/subject/jqxhbxtx.html