JavaRush /Blog Java /Random-FR /Utiliser des varargs lorsque vous travaillez avec des gén...

Utiliser des varargs lorsque vous travaillez avec des génériques

Publié dans le groupe Random-FR
Bonjour! Dans la leçon d'aujourd'hui, nous continuerons à étudier les génériques. Il se trouve que c'est un sujet important, mais il n'y a nulle part où aller - c'est une partie extrêmement importante du langage :) Lorsque vous étudiez la documentation Oracle sur les génériques ou lisez des guides sur Internet, vous rencontrerez les termes Types non réifiables et types réifiables . Quel genre de mot est « réifiable » ? Même si tout va bien avec l'anglais, il est peu probable que vous l'ayez rencontré. Essayons de traduire ! Utiliser des varargs lorsque vous travaillez avec des génériques - 2
*merci Google, tu m'as beaucoup aidé -_-*
Un type réifiable est un type dont les informations sont entièrement disponibles au moment de l'exécution. Dans le langage Java, ceux-ci incluent les primitives, les types bruts et les types non génériques. En revanche, les types non réifiables sont des types dont les informations sont effacées et rendues indisponibles au moment de l'exécution. Ce ne sont que des génériques - List<String> , List<Integer> , etc.

Au fait, vous souvenez-vous de ce que sont les varargs ?

Au cas où vous l'auriez oublié, ce sont des arguments de longueur variable. Ils sont utiles dans les situations où nous ne savons pas exactement combien d'arguments peuvent être transmis à notre méthode. Par exemple, si nous avons une classe de calculatrice et qu’elle a une méthode sum. sum()Vous pouvez transmettre 2 nombres, 3, 5 ou autant que vous le souhaitez à la méthode . Il serait très étrange de surcharger la méthode à chaque fois sum()pour prendre en compte toutes les options possibles. Au lieu de cela, nous pouvons faire ceci :
public class SimpleCalculator {

   public static int sum(int...numbers) {

       int result = 0;

       for(int i : numbers) {

           result += i;
       }

       return result;
   }

   public static void main(String[] args) {

       System.out.println(sum(1,2,3,4,5));
       System.out.println(sum(2,9));
   }
}
Sortie de la console :

15
11
Ainsi, lorsqu’il est utilisé varargsen combinaison avec des génériques, il présente certaines fonctionnalités importantes. Regardons ce code :
import javafx.util.Pair;
import java.util.ArrayList;
import java.util.List;

public class Main {

   public static <E> void addAll(List<E> list, E... array) {

       for (E element : array) {
           list.add(element);
       }
   }

   public static void main(String[] args) {
       addAll(new ArrayList<String>(),  //  здесь все нормально
               "Leonardo da Vinci",
               "Vasco de Gama"
       );

       // а здесь мы получаем предупреждение
       addAll(new ArrayList<Pair<String, String>>(),
               new Pair<String, String>("Leonardo", "da Vinci"),
               new Pair<String, String>("Vasco", "de Gama")
       );
   }
}
La méthode prend une liste et un nombre quelconque d'objets addAll()en entrée , puis ajoute tous ces objets à la liste. Dans la méthode , nous appelons notre méthode deux fois . La première fois, nous ajoutons deux lignes régulières. Tout va bien ici. La deuxième fois, nous ajoutons deux objets . Et là, nous recevons soudainement un avertissement : List<E>Emain()addAll()ListListPair<String, String>

Unchecked generics array creation for varargs parameter
Qu'est-ce que ça veut dire? Pourquoi recevons-nous un avertissement et qu'est-ce que cela a à voir avec cela array? Array- c'est un tableau, et il n'y a pas de tableaux dans notre code ! Commençons par le deuxième. L'avertissement mentionne un tableau car le compilateur convertit les arguments de longueur variable (varargs) en tableau. Autrement dit, la signature de notre méthode est addAll():
public static <E> void addAll(List<E> list, E... array)
Cela ressemble en fait à ceci :
public static <E> void addAll(List<E> list, E[] array)
Autrement dit, dans la méthode main(), le compilateur convertira notre code en ceci :
public static void main(String[] args) {
   addAll(new ArrayList<String>(),
      new String[] {
        "Leonardo da Vinci",
        "Vasco de Gama"
      }
   );
   addAll(new ArrayList<Pair<String,String>>(),
        new Pair<String,String>[] {
            new Pair<String,String>("Leonardo","da Vinci"),
            new Pair<String,String>("Vasco","de Gama")
        }
   );
}
Tout va bien avec le tableau String. Mais avec un tableau Pair<String, String>- non. Le fait est qu’il Pair<String, String>s’agit d’un type non réifiable. Lors de la compilation, toutes les informations sur les types de paramètres (<String, String>) seront effacées. La création de tableaux à partir d'un type non réifiable n'est pas autorisée en Java . Vous pouvez le vérifier si vous essayez de créer manuellement un tableau Pair<String, String>
public static void main(String[] args) {

   //  ошибка компиляции! Generic array creation
  Pair<String, String>[] array = new Pair<String, String>[10];
}
La raison est évidente : type de sécurité. Comme vous vous en souvenez, lors de la création d'un tableau, vous devez indiquer quels objets (ou primitives) ce tableau stockera.
int array[] = new int[10];
Dans l'une des leçons précédentes, nous avons examiné en détail le mécanisme d'effacement de type. Ainsi, dans ce cas, suite à l'effacement des types, nous avons perdu les informations selon lesquelles Pairles paires étaient stockées dans nos objets <String, String>. Créer un tableau sera dangereux. Lorsque vous utilisez des méthodes avec varargset des génériques, n'oubliez pas l'effacement de type et son fonctionnement exact. Si vous avez absolument confiance dans le code que vous avez écrit et que vous savez qu'il ne posera aucun problème, vous pouvez désactiver varargsles avertissements qui lui sont associés à l'aide d'une annotation.@SafeVarargs
@SafeVarargs
public static <E> void addAll(List<E> list, E... array) {

   for (E element : array) {
       list.add(element);
   }
}
Si vous ajoutez cette annotation à votre méthode, l'avertissement que nous avons rencontré précédemment n'apparaîtra pas. Un autre problème possible lors de l’utilisation varargsconjointe de génériques est la pollution massive. Utiliser des varargs lorsque vous travaillez avec des génériques - 4La contamination peut survenir dans les situations suivantes :
import java.util.ArrayList;
import java.util.List;

public class Main {

   static List<String> makeHeapPollution() {
       List numbers = new ArrayList<Number>();
       numbers.add(1);
       List<String> strings = numbers;
       strings.add("");
       return strings;
   }

   public static void main(String[] args) {

       List<String> stringsWithHeapPollution = makeHeapPollution();

       System.out.println(stringsWithHeapPollution.get(0));
   }
}
Sortie de la console :

Exception in thread "main" java.lang.ClassCastException: java.lang.Integer cannot be cast to java.lang.String
En termes simples, la pollution du tas est une situation dans laquelle les objets de type 1 devraient être sur le tas А, mais les objets de type s'y retrouvent B, en raison d'erreurs de sécurité de type. Dans notre exemple, c'est ce qui se passe. Nous avons d’abord créé une variable Raw numberset lui avons attribué une collection générique ArrayList<Number>. Après cela, nous avons ajouté le numéro ici 1.
List<String> strings = numbers;
Dans cette ligne, le compilateur a essayé de nous avertir d'éventuelles erreurs en émettant l'avertissement « Affectation non vérifiée... », mais nous l'avons ignoré. En conséquence, nous avons une variable générique de type List<String>, qui pointe vers une collection générique de type ArrayList<Number>. Cette situation peut clairement conduire à des ennuis ! C'est ce qui se passe. En utilisant notre nouvelle variable, nous ajoutons une chaîne à la collection. Le tas a été pollué - nous avons d'abord ajouté un nombre puis une chaîne à la collection tapée. Le compilateur nous a prévenus, mais nous avons ignoré son avertissement, ne recevant les résultats ClassCastExceptionque pendant l'exécution du programme. Qu'est-ce que cela a à voir avec ça varargs? L’utilisation varargsde produits génériques peut facilement entraîner une pollution massive. Voici un exemple simple :
import java.util.Arrays;
import java.util.List;

public class Main {

   static void makeHeapPollution(List<String>... stringsLists) {
       Object[] array = stringsLists;
       List<Integer> numbersList = Arrays.asList(66,22,44,12);

       array[0] = numbersList;
       String str = stringsLists[0].get(0);
   }

   public static void main(String[] args) {

       List<String> cars1 = Arrays.asList("Ford", "Fiat", "Kia");
       List<String> cars2 = Arrays.asList("Ferrari", "Bugatti", "Zaporozhets");

       makeHeapPollution(cars1, cars2);
   }
}
Que se passe t-il ici? En raison de l'effacement des caractères, nos feuilles de paramètres (nous les appellerons « feuilles » au lieu de « listes » pour plus de commodité) sont -
List<String>...stringsLists
- se transformera en un tableau de feuilles - List[]de type inconnu (n'oubliez pas que varargs se transforme en un tableau régulier suite à la compilation). De ce fait, nous pouvons facilement faire une affectation à une variable Object[] arraydans la première ligne de la méthode - les types ont été effacés de nos feuilles ! Et maintenant nous avons une variable de type Object[], à laquelle nous pouvons ajouter n'importe quoi - tous les objets en Java héritent de Object! Pour l’instant, nous ne disposons que d’un ensemble de feuilles de cordes. Mais grâce à l’utilisation varargset à l’effacement des types, nous pouvons facilement leur ajouter une feuille de nombres, ce que nous faisons. De ce fait, on pollue le tas en mélangeant des objets de types différents. Le résultat sera la même exception ClassCastExceptionlors de la tentative de lecture d'une chaîne du tableau. Sortie de la console :

Exception in thread "main" java.lang.ClassCastException: java.lang.Integer cannot be cast to java.lang.String
Telles sont les conséquences inattendues qui peuvent résulter de l'utilisation d'un mécanisme apparemment simple varargs:) Et c'est ici que se termine notre conférence d'aujourd'hui. N'oubliez pas de résoudre quelques problèmes et, s'il vous reste du temps et de l'énergie, étudiez d'autres publications. « Java efficace » ne se lit pas tout seul ! :) À bientôt!
Commentaires
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION