ArrayList prend Ssortingng

public class Main { public static void main(Ssortingng[] args) { ArrayList ar = new ArrayList(); List l = new ArrayList(); l.add("a"); l.add("b"); ar.addAll(l); System.out.println(ar); } } 

Sortie: [a,b]

Vous ne pouvez pas append directement Ssortingng à ArrayList ar , mais vous addAll() utiliser addAll() .

Comment pouvons-nous append Ssortingng à ArrayList dont le type a déjà été spécifié comme Integer ? Quelqu’un peut-il mettre en évidence des détails de mise en œuvre clairs et la raison derrière cela?

    Mais comment pouvons-nous append des chaînes à arraylist dont le type a déjà été spécifié comme Integer?

    En raison de la manière dont Java Generics a été conçu pour assurer la rétrocompatibilité, essentiellement avec les types d’effacement et les types bruts.

    Au moment de l’exécution, il n’y a rien de tel qu’un ArrayList – il y a juste un ArrayList . Vous utilisez la List types bruts, de sorte que le compilateur ne fait aucune de ses vérifications normales, que ce soit au moment de la compilation ou lors de l’ajout de versions d’exécution.

    Le compilateur vous avertit que vous faites des choses dangereuses:

     Note: Main.java uses unchecked or unsafe operations. Note: Recomstack with -Xlint:unchecked for details. 

    … et quand vous recomstackz avec le drapeau pertinent, il vous avertira de tout, y compris probablement de la ligne la plus surprenante:

     ar.addAll(l); 

    C’est celui qui me surprend un peu en termes de compilation – je crois que c’est effectivement croire que la List est une Collection Collection vraiment, quand on sait que ce n’est pas le cas.

    Si vous évitez d’utiliser des types bruts, ce type de problème disparaît.

    Cela concerne plus le mélange de types bruts et génériques dans le système de types Java que le type d’effacement. Me laisser augmenter le fragment de code de la question:

      ArrayList ar = new ArrayList(); List l = new ArrayList(); // (1) l.add("a"); l.add("b"); ar.addAll(l); // (2) System.out.println(ar); Integer i = ar.get(0); // (3) 

    Avec les génériques effacés d’aujourd’hui, la ligne (3) jette ClassCastException . Si les génériques de Java étaient réifiés, il est facile de supposer que la vérification du type à l’exécution provoque la levée d’une exception sur line (2). Ce serait une conception possible de génériques réifiés, mais d’autres conceptions pourraient ne pas effectuer cette vérification. Pourquoi pas? C’est essentiellement pour la même raison que nous avons effacé les génériques aujourd’hui: la compatibilité de migration.

    Neal Gafter a observé dans son article Reified Generics for Java qu’il y a beaucoup d’utilisations dangereuses des génériques, avec des lancers impropres, etc. Aujourd’hui, même plus de dix ans après l’introduction des génériques, je constate encore beaucoup d’utilisation de types bruts. (Y compris, malheureusement, ici sur Stack Overflow.) Effectuer une vérification de type générique réifiée de manière générique casserait une quantité énorme de code, ce qui bien sûr serait un sérieux handicap pour la compatibilité.

    Toute proposition de réification générique réaliste devrait prévoir une réification sur une base facultative, par exemple via un sous-type (comme dans la proposition de Gafter) ou des annotations (Gerakios, Biboudis, Smaragdakis. Paramètres de type réifiés à l’aide d’annotations Java. [PDF] GPSE 2013 .), et il devrait décider comment traiter les types bruts. Il semble totalement impossible d’interdire complètement les types bruts. À son tour, autoriser efficacement les types bruts signifie qu’il existe un moyen de contourner le système de types génériques.

    (Ce genre de décision n’est pas prise à la légère. J’ai assisté à des cris de correspondance entre théoriciens des types, l’un d’entre eux se plaignant du système de types de Java non fondé . Pour un théoricien des types, il s’agit de la plus grave des insultes.)

    C’est essentiellement ce que fait ce code: il contourne la qualité du contrôle de type générique en utilisant des types bruts. Même si les génériques de Java étaient réifiés, la vérification pourrait ne pas être effectuée à la ligne (2). Sous certaines conceptions de génériques réifiés, le code pourrait se comporter exactement comme il le fait aujourd’hui: en lançant une exception à la ligne (3).

    Dans la réponse de Jon Skeet , il admet être quelque peu surpris qu’à la ligne (2), le compilateur se fie à la liste contenant des éléments du type approprié. Ce n’est pas vraiment une question de confiance – après tout, le compilateur émet un avertissement ici. C’est plus le compilateur qui dit, “Ok, vous utilisez des types bruts, vous êtes tout seul. Si vous obtenez une ClassCastException plus tard, ce n’est pas de ma faute.” Encore une fois, il s’agit d’autoriser les types bruts à des fins de compatibilité, et non d’effacement.

    Vous utilisez un type brut. Si vous utilisez List l = new ArrayList<>() vous constaterez que votre code ne sera plus compilé. Les types bruts n’existent que pour la compatibilité ascendante et ne doivent pas être utilisés dans un nouveau code.

    Quand il est né, Java n’avait pas de génériques (c’est-à-dire des classes paramétrées par une autre classe). Lorsque des génériques ont été ajoutés, afin de maintenir la compatibilité, il a été décidé de ne pas modifier le format du bytecode et de la classe Java. Ainsi, les classes génériques sont transformées par le compilateur en non-génériques. Cela signifie qu’un ArrayList stocke en réalité des instances de la classe Object, et qu’il peut donc également accepter des instances de Ssortingng (c’est-à-dire une sous-classe d’Object). Le compilateur ne peut pas toujours détecter les abus.

    Vous avez omis le type de votre deuxième liste. En laissant de côté le type de la première liste, vous pouvez également faire ceci:

     ArrayList ar = new ArrayList(); ar.add(Integer.valueOf(42)); ar.add("Hello"); 

    Le type n’est pris en compte que lors de la compilation. C’est pourquoi vous pourriez recevoir un avertissement dans Eclipse. Dans le code octet, le type n’est pas pris en compte et votre application s’exécute sans exception.

    Type de sécurité que nous pouvons atteindre lors de la compilation uniquement. Pendant l’exécution, eraser effacera toutes ces choses et il s’agira d’un code d’octet normal, c’est-à-dire identique à celui des génériques.