479 votes

Possible pollution du 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 signifie exactement Eclipse quand il dit que "son utilisation pourrait potentiellement polluer le tas?"

Et

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

9 votes

0 votes

0 votes

Je vois cela dans mon éditeur : Pollution possible du tas provenant d'un type vararg paramétré

279voto

Ben Schulz Points 2791

La pollution de la heap est un terme technique. Il fait référence à des références qui ont un type qui n'est pas un supertype de l'objet auquel elles pointent.

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

Cela peut entraîner des ClassCastException "inexpliquées".

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

@SafeVarargs ne prévient pas du tout cela. Cependant, il existe des méthodes qui ne pollueront pas la heap, le compilateur ne peut tout simplement pas le prouver. Auparavant, les appelants de ces API recevaient des avertissements agaçants complètement inutiles mais devaient les supprimer à chaque site d'appel. Maintenant, l'auteur de l'API peut le supprimer une fois au niveau de la déclaration.

Cependant, si la méthode n'est en fait pas sûre, les utilisateurs ne seront plus avertis.


2 votes

Donc, est-ce que nous disons que le tas est pollué car il contient des références dont les types ne sont pas ceux auxquels nous pourrions nous attendre? (List vs List dans votre exemple)

38 votes

40 votes

Cette réponse est une excellente explication de ce qu'est la pollution du tas, mais elle n'explique pas vraiment pourquoi les varargs sont si susceptibles de la causer au point de justifier un avertissement spécifique.

272voto

Gili Points 14674

Lorsque vous déclarez

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

public static void foo(List[] bar) puis en

public static void foo(List[] bar)

Le danger survient alors que vous pourriez assigner par erreur des valeurs incorrectes à la liste et le compilateur ne déclenchera aucune erreur. Par exemple, si T est une String alors le code suivant compilera sans erreur mais échouera à l'exécution:

// Premièrement, retirez le type de tableau (les tableaux permettent ce genre de conversion ascendante)
Object[] objectArray = bar;

// Ensuite, insérez un élément avec un type incorrect dans le tableau
objectArray[0] = Arrays.asList(new Integer(42));

// Enfin, essayez d'accéder au tableau original. Une erreur d'exécution se produira
// (ClassCastException due à une conversion de Integer en String)
T firstElement = bar[0].get(0);

Si vous avez vérifié la méthode pour vous assurer qu'elle ne contient pas de failles de sécurité de ce genre alors 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 de la pollution de la mémoire à partir du paramètre varargs non reifiable

et que vous êtes certain que votre utilisation est sécuritaire alors vous devriez utiliser @SuppressWarnings("varargs") à la place. Voir Is @SafeVarargs an appropriate annotation for this method? et https://stackoverflow.com/a/14252221/14731 pour une explication détaillée de ce deuxième type d'erreur.

Références:

2 votes

Je pense que je comprends mieux. Le danger survient lorsque vous convertissez varargs en Object[]. Tant que vous ne convertissez pas en Object[], il semble que vous devriez être bien.

3 votes

Comme exemple d'une chose stupide que vous pourriez faire: static void bar(T...args) { ((Object[])args)[0] = "a"; }. Et puis appelez bar(Arrays.asList(1,2));.

0 votes

Cela ne compile pas étant donné que le type de bar[0] est une liste, pas T.

10voto

jontro Points 3423

@SafeVarargs ne l'empêche pas de se produire, 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 en effectuant une opération sur une interface générique et qu'elle contient un autre type que celui déclaré.

0 votes

Les restrictions supplémentaires du compilateur concernant son utilisation ne semblent pas particulièrement pertinentes.

6voto

Peter Lawrey Points 229686

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

Grâce à l'analyse des échappements, le JIT peut optimiser la création de ce tableau. (Une des rares fois où je l'ai constaté) Il n'est pas garanti qu'il soit optimisé, mais je ne m'en préoccuperais pas sauf si vous constatez que c'est un problème dans votre profileur de mémoire.

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

6 votes

Intéressant même si cela ne répond pas vraiment à sa question sur @SafeVarargs.

1 votes

Non, ce n'est pas ce qu'est la pollution de tas. La "pollution de tas se produit lorsqu'une variable d'un type paramétré fait référence à un objet qui n'est pas de ce type paramétré." Ref: docs.oracle.com/javase/tutorial/java/generics/…

2voto

user1122069 Points 713

La raison est que varargs donne l'option d'être appelé avec un tableau d'objets non paramétré. Donc si votre type était List < A > ... , il peut aussi être appelé avec le type non-varargs List[].

Voici un exemple:

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

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

Comme vous pouvez le voir, List[] b peut contenir n'importe quel type de consommateur, et pourtant ce code se compile. Si vous utilisez varargs, alors vous êtes bon, mais si vous utilisez la définition de méthode après l'effacement de type - void test(List[]) - alors le compilateur ne vérifiera pas les types des paramètres du modèle. @SafeVarargs supprimera cet avertissement.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X