• ArrayList有参构造方法 中 elementData.getClass() != Object[].class 的探究


    在月读ArrayList源码的时候,在ArrayList的构造方法ArrayList(Collection<? extends E> c)  中看到了一行代码,感觉有些困惑,elementData.getClass() != Object[].class 为啥会不等呢?

    /**
         * Constructs a list containing the elements of the specified
         * collection, in the order they are returned by the collection's
         * iterator.
         *
         * @param c the collection whose elements are to be placed into this list
         * @throws NullPointerException if the specified collection is null
         */
        public ArrayList(Collection<? extends E> c) {
            elementData = c.toArray();
            if ((size = elementData.length) != 0) {
                // c.toArray might (incorrectly) not return Object[] (see 6260652)
                if (elementData.getClass() != Object[].class)
                    elementData = Arrays.copyOf(elementData, size, Object[].class);
            } else {
                // replace with empty array.
                this.elementData = EMPTY_ELEMENTDATA;
            }
        }

    于是就输出相关代码来进行测试验证:

         public static void main(String[] args) {
            Object[] oo = {};
            List<String> list = new ArrayList<>();
            list.add("aa");
            System.out.println(list.getClass());
            System.out.println(list.toArray().getClass());
            System.out.println(list.toArray().getClass()== oo.getClass());
            System.out.println();
    
            list = Arrays.asList("abc");
            System.out.println(list.getClass());
            System.out.println(list.toArray().getClass());
            System.out.println(list.toArray().getClass()== oo.getClass());
            System.out.println();
    
            System.out.println(oo.getClass());
        }

    运行结果:

    可以看到两次输出list的类型都不一样,一个是java.util.ArrayList,一个是java.util.Arrays$ArrayList;

    list调用toArray()方法后得到的Class类型也不一样,一个是java.lang.Object,一个是java.lang.String,

    所以用他们和Object[]的类型进行比较的时候,也不一样,一个true,一个false。

    解释:为什么两次list不一样?

    第一个是直接使用new ArrayList()实例化的对象,所以他的类型是java.uitl.ArrayList。第二个是使用的是Arrays.asList()方法初始化的对象,看方法asList()的源码,如下所示: 

    /**
         * Returns a fixed-size list backed by the specified array.  (Changes to
         * the returned list "write through" to the array.)  This method acts
         * as bridge between array-based and collection-based APIs, in
         * combination with {@link Collection#toArray}.  The returned list is
         * serializable and implements {@link RandomAccess}.
         *
         * <p>This method also provides a convenient way to create a fixed-size
         * list initialized to contain several elements:
         * <pre>
         *     List<String> stooges = Arrays.asList("Larry", "Moe", "Curly");
         * </pre>
         *
         * @param <T> the class of the objects in the array
         * @param a the array by which the list will be backed
         * @return a list view of the specified array
         */
        @SafeVarargs
        @SuppressWarnings("varargs")
        public static <T> List<T> asList(T... a) {
            return new ArrayList<>(a);
        }  

     该方法返回的是一个ArrayList,但是此ArrayList非彼ArrayList,这个返回的ArrayList是Arrays自己的一个静态内部类,如下所示:

    /**
         * @serial include
         */
        private static class ArrayList<E> extends AbstractList<E>
            implements RandomAccess, java.io.Serializable
        {
            private static final long serialVersionUID = -2764017481108945198L;
            private final E[] a;
    
            ArrayList(E[] array) {
                a = Objects.requireNonNull(array);
            }
    
           //···
        }   

    可以看到这个静态内部类实现了RandomAccess、Serializable接口,即支持快速随机访问,支持序列化,这就支持了,向java.util.ArrayList的转化。所以asList(T...  a)方法的注释上,有一句很妙的解释:

    This method acts as bridge between array-based and collection-based APIs, in combination with {@link Collection#toArray}. 

    此方法与 {@link Collection#toArray} 相结合,充当基于数组和基于集合的 API 之间的桥梁

    所以第二个list输出类型是java.util.Arrays$ArrayList  也就是Arrays自己的ArrayList,并且这个list底层也是数组。

    解释:为什么list调用toArray()方法后得到的Class类型也不一样,一个是java.lang.Object,一个是java.lang.String ??

    先看一个例子:

    public static void main(String[] args) {
            String[] s = {"a"};
            System.out.println(s.getClass());
            System.out.println();
    
            String[][] ss = {{"a"}};
            System.out.println(ss.getClass());
            System.out.println();
    
            String[] a = {"a"};
            Integer[] b = {1};
            Object[] c = {new Object()};
            System.out.println(a.getClass());
            System.out.println(b.getClass());
            System.out.println(c.getClass());
        }  

    输出如图:

     红方框中表示的是数组的维度,s是一维数组, ss是二维数组,最后看后三行,发现a、b、c输出的是String、Integer、Object,这说明了一个问题,对数组进行getClass()方法调用,得到的是数组用来存放元素的类型

    回到问题,为什么list调用toArray()方法后得到的Class类型也不一样,一个是java.lang.Object,一个是java.lang.String ??

    第一个list,也就是直接用ArrayList实例化的list,他的toArray()方法的源码,如下:

        public Object[] toArray() {
            return Arrays.copyOf(elementData, size);
        } 

    返回的是一个明确的Object类型的数组对象,因此调用第一个list的toArray()后在调用getClass()方法 输出的就是java.lang.Object

    第二个list,被Arrays.asList()实例化后的list,他的toArray()方法源码,如下:(注意这里应该是Arrays自己的ArrayList中的toArray()方法)

    private static class ArrayList<E> extends AbstractList<E>
            implements RandomAccess, java.io.Serializable
        {
            private static final long serialVersionUID = -2764017481108945198L;
            private final E[] a;
    
            ArrayList(E[] array) {
                a = Objects.requireNonNull(array);
            }
    
            @Override
            public int size() {
                return a.length;
            }
    
            @Override
            public Object[] toArray() {
                return a.clone();
            }
            //...
    }  

    可以看到Arrays的ArrayList的toArray()方法返回的是 a.clone(),而a的声明类型为 E [ ],也就是泛型化的,即ArrayList被实例化为什么类型的,a.clone()就是什么类型的。

    代码中Arrays.asList("abc"),是String[ ]类型,所以这里调用toArray()后,返回a.clone()方法得到的是一个String [ ], 对String[ ]在调用getClass()方法,得到的就是java.lang.String

    所以在java.util.ArrayList的有参构造方法中,会有一行类型判断

        /**
         * Constructs a list containing the elements of the specified
         * collection, in the order they are returned by the collection's
         * iterator.
         *
         * @param c the collection whose elements are to be placed into this list
         * @throws NullPointerException if the specified collection is null
         */
        public ArrayList(Collection<? extends E> c) {
            elementData = c.toArray();
            if ((size = elementData.length) != 0) {
                // c.toArray might (incorrectly) not return Object[] (see 6260652)
                if (elementData.getClass() != Object[].class)
                    elementData = Arrays.copyOf(elementData, size, Object[].class);
            } else {
                // replace with empty array.
                this.elementData = EMPTY_ELEMENTDATA;
            }
        }  

    所以这里要加上特殊情况,如果数组缓冲区elementData的元素个数大于0,并且类型不是Object[ ]的,就要通过Arrays.copyOf()方法,来转化为Object[ ]

  • 相关阅读:
    反射的基础详解
    数组,排序,枚举
    继承,多态,抽象,接口
    视图层 view
    常用类Object,String类详解
    模板层 Template
    自定义注解
    Django 高级
    常用类Math,StringBuffer,包装类,Date
    内部类,异常
  • 原文地址:https://www.cnblogs.com/xuchao0506/p/14971652.html
Copyright © 2020-2023  润新知