Стирание массива - PullRequest
       5

Стирание массива

0 голосов
/ 06 марта 2019

Почему следующий код потенциально не является типобезопасным (компилятор выдает предупреждение)?

class ArrayTypeErasure<T> {
    private T[] elements;

    public void setElements(List<T> elements) {
        this.elements = (T[]) elements.toArray();
    }
}

Я пытаюсь вспомнить любые ситуации, когда код не работает, но пока он толькоя, кто терпит неудачу.

Ответы [ 3 ]

2 голосов
/ 11 марта 2019

Во-первых, обратите внимание, что массивы знают свой тип компонента во время выполнения, но экземпляры универсальных классов не знают аргумента универсального типа во время выполнения.List<T> не может создать объект массива во время выполнения с типом времени выполнения T[] без дополнительной информации, поскольку он не знает, что такое T во время выполнения.Метод List#toArray(), который принимает один параметр массива, использует тип времени выполнения переданного экземпляра массива для создания массива того же типа компонента во время выполнения.Но List#toArray() без параметров всегда создает массив с типом времени выполнения Object[].Таким образом, elements.toArray() оценивает экземпляр массива, который всегда имеет тип времени выполнения Object[].

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

Например, если у вас есть метод, который возвращает this.elements как тип T[]:

public T[] getElements() {
    return this.elements;
}

и затем у вас есть вызывающая сторона, которая содержит ArrayTypeErasure<String>, и она вызывает .getElements(), она будет ожидать String[].Когда он пытается присвоить результат String[], это вызовет исключение приведения класса, поскольку тип времени выполнения объекта - Object[]:

ArrayTypeErasure<String> foo = ...
String[] bar = foo.getElements();
2 голосов
/ 06 марта 2019

Просто потому, что List#toArray() возвращает Object[], поэтому нет никакой гарантии от этого метода, что он вернет T[]

Теперь на практике это нормально, так как вы всегда знаете, что он вернеттребуемый тип

Вы можете использовать @SuppressWarnings("unchecked"), чтобы избежать появления этого предупреждения

0 голосов
/ 06 марта 2019

Из-за стирания типа вы можете разыграть List<X> с List<Y> с.

ArrayTypeErasure<String> er = new ArrayTypeErasure<>();
ArrayTypeErasure erased = er;
List intList = new List<Integer>();  // compile warnings, but
intList.add(1);
erased.setElements(intList);
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...