Java sin marcar: creación de matriz genérica sin marcar para el parámetro varargs

111

He configurado Netbeans para que muestre advertencias sin marcar en mi código Java, pero no entiendo el error en las siguientes líneas:

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);

Da:

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

Fuente del método:

/**
 * 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;
}

¿Qué es exactamente lo que está mal y cómo lo arreglaría, ya que supongo que dejar advertencias sin marcar en el código no es una buena idea?

Olvidé mencionarlo, pero estoy usando Java 7.

Editar : También veo ahora que el método tiene lo siguiente:

[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
fuente
17
Hagas lo que hagas, en Java no necesitas inicializar una matriz int recién creada con 0s ...
Thomas Mueller
1
@ThomasMueller Buen partido allí
skiwi

Respuestas:

165

Como janoh.janoh mencionó anteriormente, varargs en Java es solo un azúcar sintáctico para matrices más la creación implícita de una matriz en el sitio de llamada. Entonces

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

es en realidad

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

Pero como sabrá, new List<String>[]no está permitido en Java, por razones que se han tratado en muchas otras preguntas, pero principalmente tienen que ver con el hecho de que las matrices conocen su tipo de componente en tiempo de ejecución y verifican en tiempo de ejecución si los elementos agregados coinciden con su componente. type, pero esta verificación no es posible para tipos parametrizados.

De todos modos, en lugar de fallar, el compilador aún crea la matriz. Hace algo similar a esto:

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

Esto es potencialmente inseguro, pero no necesariamente inseguro. La mayoría de los métodos varargs simplemente iteran sobre los elementos varargs y los leen. En este caso, no le importa el tipo de tiempo de ejecución de la matriz. Este es el caso de su método. Dado que está en Java 7, debe agregar la @SafeVarargsanotación a su método y ya no recibirá esta advertencia. Esta anotación básicamente dice que este método solo se preocupa por los tipos de elementos, no por el tipo de matriz.

Sin embargo, existen algunos métodos varargs que utilizan el tipo de tiempo de ejecución de la matriz. En este caso, es potencialmente inseguro. Por eso está la advertencia.

newacct
fuente
16
Gracias no solo por mencionar SafeVarags, sino por decirnos cuándo podemos usarlo.
KitsuneYMG
12
Si no fue inmediatamente obvio para nadie (como no lo fue para mí), el Javadocs para @SafeVarargstiene un ejemplo de un método que no es seguro docs.oracle.com/javase/7/docs/api/java/lang/SafeVarargs .html
michiakig
3
Entonces @SafeVarargs, ¿ se puede usar cuando su método solo consume los elementos de la matriz y no produce (y nunca lo hará) elementos para poner en la matriz? Se debe tener especial cuidado si asigna el argumento de matriz a un campo que podría ser manipulado por otros métodos porque determinar que no se han realizado operaciones inseguras en él podría no ser trivial.
neXus
13

Porque el compilador de Java utiliza una creación de matriz implícita para varargs, y Java no permite la creación de una matriz genérica (porque el argumento de tipo no es confiable).

El código siguiente es correcto (estas operaciones se permiten con matrices), por lo que se necesita una advertencia sin marcar:

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

Vea una explicación completa aquí

Philip Voronov
fuente