web-dev-qa-db-fra.com

Pollution potentielle de tas via le paramètre varargs

Je comprends que cela se produit avec Java 7 lors de l’utilisation de varargs avec un type générique;

Mais ma question est ..

Que veut dire exactement Eclipse quand il dit "son utilisation pourrait potentiellement polluer le tas?"

Et 

Comment la nouvelle annotation @SafeVarargs empêche-t-elle cela?

377
hertzsprung

La pollution de tas est un terme technique. Il fait référence à des références dont le type n'est pas un sur-type de l'objet indiqué.

List<A> listOfAs = new ArrayList<>();
List<B> listOfBs = (List<B>)(Object)listOfAs; // points to a list of As

Cela peut conduire à "inexplicable" ClassCastExceptions.

// if the heap never gets polluted, this should never throw a CCE
B b = listOfBs.get(0); 

@SafeVarargs ne l'empêche pas du tout. Cependant, il existe des méthodes qui ne pollueront certainement pas le tas, le compilateur ne peut tout simplement pas le prouver. Auparavant, les appelants de telles API recevaient des avertissements gênants qui étaient totalement inutiles mais devaient être supprimés à chaque site d'appel. Maintenant, l’auteur de l’API peut le supprimer une fois sur le site de déclaration.

Cependant, si la méthode est réellement not safe, les utilisateurs ne seront plus avertis.

219
Ben Schulz

Quand vous déclarez

public static <T> void foo(List<T>... bar) le compilateur le convertit en

public static <T> void foo(List<T>[] bar) puis à

public static void foo(List[] bar)

Le danger est alors que vous affectez par erreur des valeurs incorrectes à la liste et que le compilateur ne déclenche aucune erreur. Par exemple, si T est un String, le code suivant sera compilé sans erreur mais échouera au moment de l'exécution:

// First, strip away the array type (arrays allow this kind of upcasting)
Object[] objectArray = bar;

// Next, insert an element with an incorrect type into the array
objectArray[0] = Arrays.asList(new Integer(42));

// Finally, try accessing the original array. A runtime error will occur
// (ClassCastException due to a casting from Integer to String)
T firstElement = bar[0].get(0);

Si vous avez examiné la méthode pour vous assurer qu'elle ne contient pas de telles vulnérabilités, vous pouvez l'annoter avec @SafeVarargs pour supprimer l'avertissement. Pour les interfaces, utilisez @SuppressWarnings("unchecked").

Si vous obtenez ce message d'erreur:

La méthode Varargs pourrait causer une pollution de tas à partir du paramètre varargs non réifiable

et vous êtes sûr que votre utilisation est sans danger, utilisez plutôt @SuppressWarnings("varargs"). Voir @SafeVarargs est-il une annotation appropriée pour cette méthode? et https://stackoverflow.com/a/14252221/14731 pour une explication de Nice de ce second type d'erreur.

Références:

220
Gili

@SafeVarargs ne l'empêche pas, mais il impose que le compilateur soit plus strict lors de la compilation du code qui l'utilise. 

http://docs.Oracle.com/javase/7/docs/api/Java/lang/SafeVarargs.html explique cela plus en détail.

La pollution du tas se produit lorsque vous obtenez une ClassCastException lors d'une opération sur une interface générique et qu'elle contient un autre type que celui déclaré.

7
jontro

Lorsque vous utilisez varargs, cela peut entraîner la création d'un Object[] pour contenir les arguments.

En raison de l'analyse d'échappement, le JIT peut optimiser la création de ce tableau. (Une des rares fois où j'ai trouvé que c'était le cas) Son optimisation n'est pas garantie, mais je ne m'en inquiéterais pas à moins que vous ne voyiez le problème dans votre profileur de mémoire.

AFAIK @SafeVarargs supprime un avertissement du compilateur et ne change pas le comportement de JIT.

5
Peter Lawrey

La raison en est que varargs donne la possibilité d’être appelé avec un tableau d’objets non paramétré. Donc, si votre type était List <A> ..., il peut également être appelé avec le type List [] non-varargs.

Voici un exemple:

public static void testCode(){
    List[] b = new List[1];
    test(b);
}

@SafeVarargs
public static void test(List<A>... a){
}

Comme vous pouvez le voir, List [] b peut contenir n’importe quel type de consommateur, et pourtant ce code est compilé. Si vous utilisez varargs, tout va bien, mais si vous utilisez la définition de la méthode après type-erasure - void test (List []), le compilateur ne vérifiera pas les types de paramètres de modèle. @SafeVarargs supprimera cet avertissement.

0
user1122069