На самом деле у ArrayList есть близнецы, но разница довольно большая!

Java задняя часть
На самом деле у ArrayList есть близнецы, но разница довольно большая!

1. Проблемы возникают

учимся сегодняArrayListКогда я нашел исходный код, я нашел такой комментарий, то есть:

c.toArray might (incorrectly) not return Object[] (see 6260652)

это значитCollectionтип коллекцииtoArray()Однако метод объявляет, что тип возвращаемого значенияObject[], но он не обязательно возвращается, когда делается конкретный вызовOnject[]типа и, возможно, других типов, это зависит от васcФактический тип при неправильном использовании вызовет исключение. Это может быть очень запутанным, я подробно объясню, почему, сейчас давайте посмотримCollectionсерединаtoArray()Постановка, чтобы у вас сначала сложилось общее впечатление об этом методе.

public Object[] toArray(); // 声明返回值类型为Object[]

Так что же происходит с вышеуказанной ошибкой? Давайте рассмотрим следующие два примера:

1、没有抛异常的情况

// 声明一个ArrayList集合,泛型为String类型
List<String> list = new ArrayList<>();
// 添加一个元素
list.add("list");
// 将上面的集合转换为对象数组
Object[] listArray = list.toArray(); ................ 1
// 输出listArray的类型,输出class [Ljava.lang.Object;
System.out.println(listArray.getClass());
// 往listArray赋值一个Onject类型的对象
listArray[0] = new Object();

2、抛异常的情况

// 同一创建一个列表,但是现在是通过Arrays工具类来创建,创建的列表类型为Arrays的内部类ArrayList类型
List<String> asList = Arrays.asList("string");
// 转换为对象数组
Object[] asListArray = asList.toArray();.............. 2
// 输出转换后元素类型,将输出class [Ljava.lang.String;
System.out.println(asListArray.getClass());
// 往对象数组中添加Object类型对象,会报错java.lang.ArrayStoreException
asListArray[0] = new Object();

Первый случай вышеnew ArrayList()созданjava.util.ArrayListтип, второй способ - использоватьArrays.asList()созданjava.util.Arrays$ArrayListтип, оба имени типаArrayList, но реализация действительно отличается. Тогда почему он должен сообщать об ошибке? в конечном счетеtoArray()Реализация этого метода отличается. Давайте посмотрим на каждыйjava.util.ArrayListКатегорияtoArray()а такжеjava.util.Arrays$ArrayListизtoArray()реализация:

java.util.ArrayList

public Object[] toArray() {
    // 调用Arrays工具类进行数组拷贝
    return Arrays.copyOf(elementData, size);...............1
}

Arrays.copyOf()

public static <T> T[] copyOf(T[] original, int newLength) {
    return (T[]) copyOf(original, newLength, original.getClass());.................2
}
public static <T,U> T[] copyOf(U[] original, int newLength, Class<? extends T[]> newType) {
    // 在创建新数组对象之前会先对传入的数据类型进行判定
    @SuppressWarnings("unchecked")
    T[] copy = ((Object)newType == (Object)Object[].class)
        ? (T[]) new Object[newLength]
        : (T[]) Array.newInstance(newType.getComponentType(), newLength);
    System.arraycopy(original, 0, copy, 0,
                     Math.min(original.length, newLength));
    return copy;
}

Нижеjava.util.Arrays$ArrayListреализация

private final E[] a;
@Override
public Object[] toArray() {
    return a.clone();
}

Как видно из вышеизложенного, вjava.util.ArrayListбудет вызванArraysизcopyOf()метод, переходящий вnewTypeделать суждения о типах,newTypeценностьoriginal.getClass(),из-заoriginal.getClass()Возвращаемый типObject[](конкретно см.ArrayListИсходный код известен), поэтому звонитеtoArray()вернетObject[]введите массив, поэтому перейдите кlistArrayотбросить один в переменнойObjectОбъекты типа, конечно, не сообщат об ошибке.

посмотри сноваjava.util.Arrays$ArrayListВидно, что тип данных определен как универсальный.E,EКонкретный тип будет определяться на основе фактического типа, который вы передаете, поскольку вы передали"string", поэтому фактический типString[], конечно звонюa.clone()Затем верните то же самоеString[]типа, здесь просто делается приведение, котороеString[]введите вObject[]Тип возвращается, но обратите внимание, что хотя возвращаемый引用заObject[], но фактический тип по-прежнемуString[], когда вы идете в引用Сообщается об ошибке, когда к объекту добавляется элемент, тип которого не соответствует фактическому типу. Недовольны? Вот каштан:

// 数组strings为String[]类型
String[] strings = { "a", "b" };
// 向上转型为Object[]类型,那么这个objects就属于引用类型为Object[],而实际类型为String[]
Object[] objects = strings;
// 添加一个Object类型变量,就报错啦!
objects[0] = new Object();//! java.lang.ArrayStoreException

Чтобы углубить наше понимание, давайте суммируем разницу между повышением и понижением приведения в java. Мы все знаем, что мы можем вводитьFather fa = new Son()заявлено в порядкеFatherТипSonРодительский класс типа, то есть восходящее преобразование происходит, а восходящее преобразование выполняется автоматически в java, никакого принуждения не требуется, и исключение не выбрасывается. Downcasting делится на два случая, следующие в сочетании с демонстрацией кода:

// 向上转型
Father fa = new Son();

Father fafa = new Father();

// 向下转型(不会报错)
Son son = (Son) fa;.................1

// 向下转型,报错了java.lang.ClassCastException
Son sonson = (Son) fafa;.......................2

Его можно найти1ошибка не будет сообщена,2ошибка, потому что1кудаfaФактический тип переменнойSon, тип ссылкиFather, понижение зависит от фактического типа, а не от ссылочного типа, например.fafaФактический тип переменной сам по себеFather, В Java по умолчанию родительский класс не может быть приведен к дочернему классу.

2. Резюме

Прежде всего, самые важные вещи:

  • 1. После преобразования коллекции массивов в Java в коллекцию массивов нельзя добавить объект ссылочного типа (то есть родительского типа), а можно добавить объект фактического типа, например ``Отец[] отец = сын [],你就不能往father中添加Father类型了,而应该是Son`

  • 2. Повышение приведения в Java разрешено по умолчанию, но понижение приведения может привести к ошибкам, так что будьте осторожны!

  • 3. Будьте осторожны сArrays.asList()Созданный тип коллекции неjava.util.ArrayList, ноjava.util.Arrays$ArrayList, Многие методы двух классов реализованы по-разному.

Спасибо за прочтение и добро пожаловать в раздел комментариев!