Java warning: метод Varargs может вызвать загрязнение кучи от невосстанавливаемого параметра varargs

Я использую IntelliJ IDEA с javac на JDK 1.8. У меня есть следующий код:

class Test<T extends Throwable>
{
    @SafeVarargs
    final void varargsMethod( Collection<T>... varargs )
    {
        arrayMethod( varargs );
    }

    void arrayMethod( Collection<T>[] args )
    {
    }
}

IntelliJ IDEA не выделяет ничего в приведенном выше коде как предупреждение. Однако при компиляции следующая строка появляется на вкладке "Сделать" в представлении "Сообщения":

Предупреждение: (L, C) java: метод Varargs может вызвать загрязнение кучи от невосстанавливаемый параметр varargs varargs

Примечание №1: Я уже указал @SafeVarargs.

Примечание # 2: Warning:(L,C) указывает на varargs передается как аргумент arrayMethod()

Предполагая, что я знаю, что я делаю, и полагаю, что я уверен, что не будет никакого загрязнения кучи или я обещаю, что я не буду называть этот метод каким-то напуганным способом, который может привести к загрязнению кучи, что мне нужно сделать, чтобы подавить это предупреждающее сообщение?

ПРИМЕЧАНИЕ.. Существует множество вопросов о stackoverflow относительно методов varargs, но, похоже, что нет этой проблемы. На самом деле, весь interwebz, кажется, довольно беден в ответах на этот конкретный вопрос.

Ответ 1

Это может объяснить причину. Я просто скопировал его из Effective Java 2nd Edition, Item 25. Надеюсь, это может помочь.

Запрет на создание массивных массивов может быть раздражающим. Это означает, например, что обычно не возможно, чтобы общий тип возвращал массив своего типа элемента (но см. Пункт 29 для частичного решения). Это также означает, что вы можете вводить путаные предупреждения при использовании методов varargs (пункт 42) в сочетании с типичными типами. Это происходит потому, что каждый раз, когда вы вызываете метод varargs, создается массив для хранения параметров varargs. Если тип элемента этого массива не может быть повторно идентифицирован, вы получите предупреждение.. Мало того, что вы можете делать с этими предупреждениями, кроме как подавлять их (пункт 24), и избегать смешивания генераторов и varargs в ваших API.

Ответ 2

Ни один из ответов, которые я видел по этому вопросу, мне кажутся удовлетворительными, поэтому я подумал, что я нанесу удар по нему.

Вот как я его вижу:

  • @SafeVarargs

    • Это предупреждение для предупреждения: [unchecked] Possible heap pollution from parameterized vararg type Foo.
    • Является частью контракта метода, поэтому аннотация имеет задержку во время выполнения.
    • Является обещанием вызывающего метода, что метод не испортит кучу, используя общий аргумент varargs.
  • @SuppressWarnings("varargs")

    • Это предупреждение для предупреждения: [varargs] Varargs method could cause heap pollution from non-reifiable varargs parameter bar.
    • Является средством для устранения проблем, возникающих в коде метода, а не в контракте метода, поэтому в аннотации в bar(), чтобы уведомить об этом предупреждение о bar().

Ответ 3

Для подавления предупреждения требуется дополнительное (и совершенно ненужное) @SuppressWarnings( "varargs" ):

@SafeVarargs
@SuppressWarnings( "varargs" )
final void varargsMethod( Collection<T>... varargs )
{
    arrayMethod( varargs );
}

Ответ 4

На самом деле вы не должны писать свой код таким образом. Рассмотрим следующий пример:

import java.util.*;

class Test<T extends Throwable>
{
    @SafeVarargs
    @SuppressWarnings("varargs")
    final void varargsMethod( Collection<T>... varargs )
    {
        arrayMethod( varargs );
    }

    void arrayMethod( Collection<T>[] args )
    {
        Object[] array = args;
        array[1] = new Integer(1);
        //
        //ArrayList<Integer> list = new ArrayList<>();
        //list.add(new Integer(1));
        //array[1] = list;
    }

    public static void main(String[] args)
    {
        ArrayList<Exception> list1 = new ArrayList<>();
        ArrayList<Exception> list2 = new ArrayList<>();
        (new Test<Exception>()).varargsMethod(list1, list2);
    }
}

Если вы запустите код, вы увидите исключение ArrayStoreException, потому что вы поместите Integer в массив Collection<T>.

Однако, если вы замените массив [1] = новый Integer (1); с тремя строками комментария (т.е. положить ArrayList<Integer> в массив), из-за стирания типа исключение не генерируется и ошибка компиляции не возникает.

Вы хотите иметь массив Collection<Exception>, но теперь он содержит ArrayList<Integer>. Это довольно опасно, так как вы не поймете, что есть проблема.

Ответ 5

Предполагая, что я знаю, что я делаю

Я отказываюсь принимать это, поскольку это кажется невероятно неправильным.

Ситуация, в которой вы работаете, заключается в том, что, поскольку генерики не могут быть повторно идентифицируемы, вы объявляете общий массив, который, как правило, не одобряется. Генерики и массивы не смешиваются очень хорошо, учитывая, что массив ковариантный (String[] является Object[] таким же образом, что a String является Object), тогда как generics инвариант (List<String> не является List<Object>, хотя a String является Object).

Если вам нужна коллекция коллекций... просто передайте один. Это безопаснее, чем смешивание массивов и дженериков.

final void varargsMethod(Collection<<Collection<? super T>> collections) { }