Java niezaznaczona: niezaznaczone tworzenie ogólnych tablic dla parametru varargs

112

Ustawiłem Netbeans tak, aby wyświetlał niesprawdzone ostrzeżenia w moim kodzie Java, ale nie rozumiem błędu w następujących wierszach:

private List<String> cocNumbers;
private List<String> vatNumbers;
private List<String> ibans;
private List<String> banks;
...
List<List<String>> combinations = Utils.createCombinations(cocNumbers, vatNumbers, ibans);

Daje:

[unchecked] unchecked generic array creation for varargs parameter of type List<String>[]

Źródło metody:

/**
 * Returns a list of all possible combinations of the entered array of lists.
 *
 * Example: [["A", "B"], ["0", "1", "2"]]
 * Returns: [["A", "0"], ["A", "1"], ["A", "2"], ["B", "0"], ["B", "1"], ["B", "2"]]
 *
 * @param <T> The type parameter
 * @param elements An array of lists
 * @return All possible combinations of the entered lists
 */
public static <T> List<List<T>> createCombinations(List<T>... elements) {
    List<List<T>> returnLists = new ArrayList<>();

    int[] indices = new int[elements.length];
    for (int i = 0; i < indices.length; i++) {
        indices[i] = 0;
    }

    returnLists.add(generateCombination(indices, elements));
    while (returnLists.size() < countCombinations(elements)) {
        gotoNextIndex(indices, elements);
        returnLists.add(generateCombination(indices, elements));
    }

    return returnLists;
}

Co dokładnie idzie nie tak i jak mam to naprawić, skoro wydaje mi się, że pozostawienie w kodzie niezaznaczonych ostrzeżeń nie jest dobrym pomysłem?

Zapomniałem o tym wspomnieć, ale używam Java 7.

Edycja : Widzę również, że metoda ma następujące cechy:

[unchecked] Possible heap pollution from parameterized vararg type List<T>
  where T is a type-variable:
    T extends Object declared in method <T>createCombinations(List<T>...)
skiwi
źródło
17
Cokolwiek robisz, w Javie nie musisz inicjować nowo utworzonej tablicy int z zerami ...
Thomas Mueller
1
@ThomasMueller Dobry chwyt
skiwi

Odpowiedzi:

165

Jak wspomniano powyżej w janoh.janoh, varargs w Javie to tylko cukier składniowy dla tablic oraz niejawne tworzenie tablicy w witrynie wywołującej. Więc

List<List<String>> combinations =
    Utils.createCombinations(cocNumbers, vatNumbers, ibans);

jest aktualne

List<List<String>> combinations =
    Utils.createCombinations(new List<String>[]{cocNumbers, vatNumbers, ibans});

Ale jak być może wiesz, new List<String>[]nie jest dozwolone w Javie z powodów, które zostały omówione w wielu innych pytaniach, ale głównie z faktu, że tablice znają swój typ komponentu w czasie wykonywania i sprawdzają w czasie wykonywania, czy dodane elementy pasują do swojego komponentu typ, ale to sprawdzenie nie jest możliwe w przypadku typów sparametryzowanych.

W każdym razie, zamiast zawieść, kompilator nadal tworzy tablicę. Robi coś podobnego do tego:

List<List<String>> combinations =
    Utils.createCombinations((List<String>[])new List<?>[]{cocNumbers, vatNumbers, ibans});

Jest to potencjalnie niebezpieczne, ale niekoniecznie niebezpieczne. Większość metod varargs po prostu iteruje po elementach varargs i czyta je. W tym przypadku nie dba o typ środowiska wykonawczego tablicy. Tak jest w przypadku Twojej metody. Ponieważ korzystasz z Javy 7, powinieneś dodać @SafeVarargsadnotację do swojej metody, a nie będziesz już otrzymywać tego ostrzeżenia. Ta adnotacja zasadniczo mówi, że ta metoda dba tylko o typy elementów, a nie o typ tablicy.

Jednak istnieje kilka metod varargs, które używają typu środowiska wykonawczego tablicy. W takim przypadku jest to potencjalnie niebezpieczne. Dlatego jest tam ostrzeżenie.

newacct
źródło
16
Dziękujemy nie tylko za wspomnienie o SafeVarags, ale także za poinformowanie nas, kiedy możemy go użyć.
KitsuneYMG
12
Jeśli nie było to od razu oczywiste dla nikogo (jak nie dla mnie), Javadocs dla @SafeVarargsma przykład metody, która jest niebezpieczna docs.oracle.com/javase/7/docs/api/java/lang/SafeVarargs .html
michiakig
3
Więc @SafeVarargsmoże być używany, gdy twoja metoda zużywa tylko elementy tablicy i nie produkuje (i nigdy nie będzie) wytwarzać elementów do umieszczenia w tablicy? Należy zachować szczególną ostrożność, jeśli przypisujesz argument tablicowy do pola, którym można manipulować innymi metodami, ponieważ ustalenie, że nie są wykonywane na nim niebezpieczne operacje, może nie być trywialne.
neXus
13

Ponieważ kompilator java używa niejawnego tworzenia tablicy dla varargs, a java nie zezwala na tworzenie ogólnej tablicy (ponieważ argument typu nie jest reifable).

Poniższy kod jest poprawny (te operacje są dozwolone w przypadku tablic), dlatego potrzebne jest niezaznaczone ostrzeżenie:

public static <T> List<List<T>> createCombinations(List<T> ... lists) {
    ((Object[]) lists)[0] = new ArrayList<Integer>();
    // place your code here
}

Zobacz obszerne wyjaśnienie tutaj

Philip Voronov
źródło