Почему оператор Diamond не работает в вызове addAll () в Java 7? - PullRequest
11 голосов
/ 26 сентября 2011

Данный пример из учебного пособия по обобщению .

List<String> list = new ArrayList<>();
list.add("A");

// The following statement should fail since addAll expects
// Collection<? extends String>

list.addAll(new ArrayList<>());

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

Пожалуйста, объясните подробно.

Ответы [ 3 ]

14 голосов
/ 26 сентября 2011

Прежде всего: если вы не используете Java 7, все это не будет работать, потому что ромб <> был представлен только в этой версии Java.

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

Алмаз на самом деле является сокращением для того, чтобы не повторять информацию об общем типе прикомпилятор может определить тип самостоятельно.

Наиболее распространенный вариант использования - это когда переменная определена в той же строке, что и инициализированная:

List<String> list = new ArrayList<>(); // is a shortcut for
List<String> list = new ArrayList<String>();

В этом примере разница неЭто не главное, но как только вы доберетесь до Map<String, ThreadLocal<Collection<Map<String,String>>>>, это будет значительное улучшение (примечание: я не поощряю использование таких конструкций!).

Проблема в том, что правила заходят так далеко.В приведенном выше примере совершенно очевидно, какой тип следует использовать, и компилятор, и разработчик согласны с этим.

В этой строке:

list.addAll(new ArrayList<>());

это кажется очевидно.По крайней мере, разработчик знает, что тип должен быть String.

Однако, взглянув на определение Collection.addAll(), мы увидим тип параметра Collection<? extends E>.

Это означает, что addAll принимает любую коллекцию, которая содержит объекты любого неизвестного типа, который расширяет тип нашего list.Это хорошо, потому что это означает, что вы можете addAll a List<Integer> до List<Number>, но это делает наш вывод типа более сложным.

Фактически это делает вывод типа не работающим в соответствии с правилами, изложенными в настоящее время.по JLS.В некоторых ситуациях можно утверждать, что правила могут быть расширены для работы, но текущие правила подразумевают, что не делают этого.

1 голос
/ 26 сентября 2011

Объяснение из документации Type Inference , кажется, отвечает на этот вопрос напрямую (если я не пропускаю что-то еще).

Java SE 7 и более поздние версии поддерживают ограниченный вывод типа для универсального экземплярасоздание;вы можете использовать вывод типа, только если параметризованный тип конструктора очевиден из контекста.Например, следующий пример не компилируется:

List<String> list = new ArrayList<>();
list.add("A");

  // The following statement should fail since addAll expects
  // Collection<? extends String>

list.addAll(new ArrayList<>());

Обратите внимание, что ромб часто работает в вызовах методов; однако для большей ясности предлагается использовать ромб в основном для инициализации переменной, в которой он объявлен .

Для сравнения компилируется следующий пример:

// The following statements compile:

List<? extends String> list2 = new ArrayList<>();
list.addAll(list2);
0 голосов
/ 27 сентября 2011

При компиляции вызова метода javac должен сначала узнать тип аргументов, прежде чем определять, какая сигнатура метода соответствует им. Таким образом, тип параметра метода неизвестен до того, как известен тип аргумента.

Может быть, это можно улучшить; на сегодняшний день тип аргумента не зависит от контекста.

...