Подтвердить что ты не робот

Нечетное поведение дженериков List.toArray(T [])

Сегодня я столкнулся с чем-то очень простым, но чрезвычайно сбивающим с толку. Мне нужно было преобразовать список в массив. Список содержит экземпляры String. Прекрасный пример использования List.toArray(T[]), поскольку мне нужен экземпляр String[]. Однако это не сработало бы, если бы явное выражение результата не было String[].

В качестве тестового сценария я использовал следующий код:

import java.util.Arrays;
import java.util.List;

public class MainClass {
    public static void main(String args[]) {
        List l = Arrays.asList("a", "b", "c");
        String stuff[] = l.toArray(new String[0]);
        System.err.println(Arrays.asList(stuff));
    }
}

который не компилируется. Это почти точная копия примера в javadoc, но компилятор говорит следующее:

MainClass.java:7: incompatible types
found   : java.lang.Object[]
required: java.lang.String[]
    String stuff[] = l.toArray(new String[0]);
                          ^

Если я добавлю актерский состав в String[], он будет компилироваться и работать отлично. Но это не то, что я ожидаю, когда смотрю на подпись метода toArray:

<T> T[] toArray(T[] a)

Это говорит мне, что мне не нужно бросать. Что происходит?

Edit:

Любопытно, если я изменил объявление списка на:

List<?> l = Arrays.asList("a", "b", "c");

он также работает. Или List<Object>. Таким образом, это не должно быть List<String>, как было предложено. Я начинаю думать, что использование raw List также влияет на то, как работают общие методы внутри этого класса.

Второе редактирование:

Думаю, я понял это сейчас. То, что Том Хотин написал в комментарии ниже, кажется лучшим объяснением. Если вы используете общий тип необработанным способом, вся информация генерических файлов из этого экземпляра будет удалена компилятором.

4b9b3361

Ответ 1

вы забыли указать параметр типа для своего списка:

List<String> l = Arrays.asList("a", "b", "c");

в этом случае вы можете написать безопасность:

String[] a = l.toArray(new String[0]);

без каста.

Ответ 2

или do

List<?> l = Arrays.asList("a", "b", "c");  

все еще странно

Ответ 3

List<String> l = Arrays.asList("a", "b", "c");

это заставит его скомпилировать, вы используете generics, чтобы сказать "это список строк", поэтому метод toArray знает, какой тип массива должен возвращаться.

Ответ 4

Это потому, что ваш список содержит объекты, а не строки. Если бы вы объявили свой список как List<String>, компилятор был бы счастлив.

Ответ 5

List без объявленного типа по умолчанию будет "Список объектов", а List<?> означает "Список неизвестных". В мире родовых типов факт, что "Список объектов" отличается от "List of String", но компилятор не может сказать то же самое для "Список неизвестных". Если вы объявили его неизвестным, то, насколько компилятор может сказать, что ОК.

Главное, что объявление чего-то в качестве подстановочного знака? отличается от объявления его как объекта. Подробнее о подстановочных знаках здесь