Qu’est-ce qu’un type brut et pourquoi ne devrions-nous pas l’utiliser?

Des questions:

  • Quels sont les types bruts en Java et pourquoi entends-je souvent qu’ils ne devraient pas être utilisés dans un nouveau code?
  • Quelle est l’alternative si nous ne pouvons pas utiliser les types bruts, et comment ça se passe?

Qu’est-ce qu’un type brut?

La spécification de langage Java définit un type brut comme suit:

JLS 4.8 Raw Types

Un type brut est défini comme étant l’un des suivants:

  • Le type de référence qui est formé en prenant le nom d’une déclaration de type générique sans liste d’arguments de type associée.

  • Un type de tableau dont le type d’élément est un type brut.

  • Un type de membre non static d’un type brut R qui n’est pas hérité d’une super-classe ou d’une super-interface de R

Voici un exemple pour illustrer:

 public class MyType { class Inner { } static class Nested { } public static void main(Ssortingng[] args) { MyType mt; // warning: MyType is a raw type MyType.Inner inn; // warning: MyType.Inner is a raw type MyType.Nested nest; // no warning: not parameterized type MyType mt1; // no warning: type parameter given MyType mt2; // no warning: type parameter given (wildcard OK!) } } 

Ici, MyType est un type paramétré ( JLS 4.5 ). Il est courant de se référer familièrement à ce type simplement en tant que MyType , mais techniquement, le nom est MyType .

mt a un type brut (et génère un avertissement de compilation) par le premier sharepoint la définition ci-dessus; inn également un type brut au troisième point.

MyType.Nested n’est pas un type paramétré, même s’il s’agit d’un type membre de type paramétré MyType , car il est static .

mt1 et mt2 sont tous deux déclarés avec des parameters de type réels, ce ne sont donc pas des types bruts.


Quelle est la particularité des types bruts?

Essentiellement, les types bruts se comportent comme ils l’étaient avant l’introduction des génériques. Autrement dit, ce qui suit est entièrement légal à la compilation.

 List names = new ArrayList(); // warning: raw type! names.add("John"); names.add("Mary"); names.add(Boolean.FALSE); // not a compilation error! 

Le code ci-dessus fonctionne correctement, mais supposons que vous ayez également les éléments suivants:

 for (Object o : names) { Ssortingng name = (Ssortingng) o; System.out.println(name); } // throws ClassCastException! // java.lang.Boolean cannot be cast to java.lang.Ssortingng 

Maintenant, nous rencontrons des problèmes à l’exécution, car les names contiennent quelque chose qui n’est pas une instanceof Ssortingng .

Vraisemblablement, si vous voulez que les names contiennent uniquement des Ssortingng , vous pouvez peut être utiliser un type brut et vérifier manuellement chaque add , puis le convertir manuellement en Ssortingng pour chaque élément à partir de names . Mieux encore , il ne faut PAS utiliser un type brut et laisser le compilateur faire tout le travail pour vous , exploitant la puissance des génériques Java.

 List names = new ArrayList(); names.add("John"); names.add("Mary"); names.add(Boolean.FALSE); // compilation error! 

Bien sûr, si vous souhaitez que les names autorisent un Boolean , vous pouvez le déclarer en tant que noms Listnames et le code ci-dessus sera compilé.

Voir également

  • Tutoriels Java / Génériques

En quoi un type brut diffère-t-il de l’utilisation de comme paramètre de type?

Voici une citation de Effective Java 2nd Edition, article 23: Ne pas utiliser les types bruts dans le nouveau code :

Quelle est la différence entre le type brut List et le type paramétré List ? En gros, le premier a opté pour la vérification de type générique, tandis que le second a explicitement indiqué au compilateur qu’il était capable de contenir des objects de tout type. Bien que vous puissiez transmettre un List à un paramètre de type List , vous ne pouvez pas le transmettre à un paramètre de type List . Il existe des règles de sous-typage pour les génériques et List est un sous-type de la List types bruts, mais pas du type paramétré List . En conséquence, vous perdez la sécurité de type si vous utilisez un type brut comme List , mais pas si vous utilisez un type paramétré tel que List .

Pour illustrer ce point, considérez la méthode suivante qui prend un List et ajoute un new Object() .

 void appendNewObject(List list) { list.add(new Object()); } 

Les génériques en Java sont invariants. Une List n’est pas une List . Par conséquent, le message suivant génère un avertissement du compilateur:

 List names = new ArrayList(); appendNewObject(names); // compilation error! 

Si vous aviez déclaré appendNewObject de prendre un type de List brute en paramètre, cela comstackrait, et vous perdriez donc le type de sécurité que vous obtenez des génériques.

Voir également

  • Quelle est la différence entre et ?
  • covariance des génériques java (non)

En quoi un type brut diffère-t-il de l’utilisation de Comme paramètre de type?

List , List , etc sont tous List , Il peut donc être tentant de dire qu’ils sont juste List place. Cependant, il y a une différence majeure: comme une List ne définit que l’ add(E) , vous ne pouvez append aucun object arbitraire à une List . D’un autre côté, puisque la List types bruts n’a pas de sécurité de type, vous pouvez add peu près n’importe quoi à une List .

Considérez la variation suivante de l’extrait de code précédent:

 static void appendNewObject(List list) { list.add(new Object()); // compilation error! } //... List names = new ArrayList(); appendNewObject(names); // this part is fine! 

Le compilateur a fait un travail formidable en vous protégeant de la violation potentielle de l’invariance de type de la List ! Si vous aviez déclaré le paramètre comme List list types bruts, le code serait compilé et vous violeriez le type invariant des List names .


Un type brut est l’effacement de ce type

Retour à JLS 4.8:

Il est possible d’utiliser comme type l’effacement d’un type paramétré ou l’effacement d’un type de tableau dont le type d’élément est un type paramétré. Un tel type est appelé un type brut .

[…]

Les superclasses (respectivement superinterfaces) d’un type brut sont les effacements des superclasses (superinterfaces) de l’une des paramésortingsations du type générique.

Le type d’un constructeur, d’une méthode d’instance ou d’ static champ non static d’un type brut C qui n’est pas hérité de ses superclasses ou superinterfaces est le type brut qui correspond à l’effacement de son type dans la déclaration générique correspondant à C

En termes plus simples, lorsqu’un type brut est utilisé, les constructeurs, les méthodes d’instance et static champs non static sont également effacés .

Prenons l’exemple suivant:

 class MyType { List getNames() { return Arrays.asList("John", "Mary"); } public static void main(Ssortingng[] args) { MyType rawType = new MyType(); // unchecked warning! // required: List found: List List names = rawType.getNames(); // compilation error! // incompatible types: Object cannot be converted to Ssortingng for (Ssortingng str : rawType.getNames()) System.out.print(str); } } 

Lorsque nous utilisons le brut MyType , getNames est également effacé, de sorte qu’il renvoie une List brute!

JLS 4.6 continue à expliquer ce qui suit:

L’effacement de type mappe également la signature d’un constructeur ou d’une méthode sur une signature sans type paramétré ni variable de type. L’effacement d’un constructeur ou d’une signature de méthode s est une signature composée du même nom que s et des effacements de tous les types de parameters formels donnés dans s .

Le type de retour d’une méthode et les parameters de type d’une méthode générique ou d’un constructeur sont également effacés si la signature du constructeur ou de la méthode est effacée.

L’effacement de la signature d’une méthode générique n’a pas de parameters de type.

Le rapport de bogue suivant contient des reflections de Maurizio Cimadamore, un développeur du compilateur, et Alex Buckley, l’un des auteurs du JLS, sur les raisons pour lesquelles ce type de comportement devrait se produire: https://bugs.openjdk.java.net/browse / JDK-6400189 . (En bref, cela simplifie la spécification.)


Si c’est dangereux, pourquoi est-il permis d’utiliser un type brut?

Voici une autre citation de JLS 4.8:

L’utilisation de types bruts est autorisée uniquement en tant que concession à la compatibilité du code hérité. L’utilisation de types bruts dans le code écrit après l’introduction de la généricité dans le langage de programmation Java est fortement déconseillée. Il est possible que les futures versions du langage de programmation Java interdisent l’utilisation de types bruts.

Java 2e édition efficace a aussi ceci à append:

Étant donné que vous ne devriez pas utiliser de types bruts, pourquoi les concepteurs de langage les ont-ils autorisés? Pour assurer la compatibilité

La plate-forme Java était sur le point d’entrer dans sa deuxième décennie lorsque les génériques ont été introduits, et il existait une énorme quantité de code Java qui n’utilisait pas de génériques. Il a été jugé essentiel que tout ce code rest légal et interopérable avec le nouveau code qui utilise des génériques. Il devait être légal de passer des instances de types paramétrés à des méthodes conçues pour être utilisées avec des types ordinaires, et inversement. Cette exigence, appelée compatibilité de migration , a conduit à la décision de prendre en charge les types bruts.

En résumé, les types bruts ne doivent JAMAIS être utilisés dans un nouveau code. Vous devez toujours utiliser des types paramétrés .


N’y a-t-il pas d’exceptions?

Malheureusement, les génériques Java n’étant pas réifiés, il existe deux exceptions où les types bruts doivent être utilisés dans le nouveau code:

  • List.class classe, par exemple List.class , pas List.class
  • instanceof opérande, par exemple o instanceof Set , pas o instanceof Set

Voir également

  • Pourquoi Collection.class illégale?

Quels sont les types bruts en Java et pourquoi entends-je souvent qu’ils ne devraient pas être utilisés dans un nouveau code?

Les types bruts sont une histoire ancienne du langage Java. Au début, il y avait des Collections et ils détenaient des Objects rien de plus et rien de moins. Chaque opération sur les Collections nécessite la conversion de l’ Object au type souhaité.

 List aList = new ArrayList(); Ssortingng s = "Hello World!"; aList.add(s); Ssortingng c = (Ssortingng)aList.get(0); 

Bien que cela ait fonctionné la plupart du temps, des erreurs se sont produites

 List aNumberList = new ArrayList(); Ssortingng one = "1";//Number one aNumberList.add(one); Integer iOne = (Integer)aNumberList.get(0);//Insert ClassCastException here 

Les anciennes collections sans typage ne pouvaient pas appliquer la sécurité de type, le programmeur devait donc se souvenir de ce qu’il stockait dans une collection.
Génériques où inventé pour contourner cette limitation, le développeur déclarerait le type stocké une fois et le compilateur le ferait à la place.

 List aNumberList = new ArrayList(); aNumberList.add("one"); Integer iOne = aNumberList.get(0);//Comstack time error Ssortingng sOne = aNumberList.get(0);//works fine 

En comparaison:

 // Old style collections now known as raw types List aList = new ArrayList(); //Could contain anything // New style collections with Generics List aList = new ArrayList(); //Contains only Ssortingngs 

Plus complexe l’interface Comparable:

 //raw, not type save can compare with Other classes class MyCompareAble implements CompareAble { int id; public int compareTo(Object other) {return this.id - ((MyCompareAble)other).id;} } //Generic class MyCompareAble implements CompareAble { int id; public int compareTo(MyCompareAble other) {return this.id - other.id;} } 

Notez qu’il est impossible d’implémenter l’interface compareTo(MyCompareAble) avec compareTo(MyCompareAble) avec les types bruts. Pourquoi ne pas les utiliser:

  • Tout Object stocké dans une Collection doit être converti avant de pouvoir être utilisé
  • Utiliser des génériques permet de vérifier les temps de compilation
  • L’utilisation de types bruts est la même chose que le stockage de chaque valeur en tant Object

Ce que fait le compilateur: les génériques sont rétrocompatibles, ils utilisent les mêmes classes Java que les types bruts. La magie se produit principalement au moment de la compilation.

 List someSsortingngs = new ArrayList(); someSsortingngs.add("one"); Ssortingng one = someSsortingngs.get(0); 

Sera compilé comme:

 List someSsortingngs = new ArrayList(); someSsortingngs.add("one"); Ssortingng one = (Ssortingng)someSsortingngs.get(0); 

C’est le même code que vous écririez si vous utilisiez directement les types bruts. Je pense que je ne suis pas sûr de ce qui se passe avec l’interface CompareAble , je suppose que cela crée deux fonctions compareTo , l’une prenant un MyCompareAble et l’autre prenant un Object et le transmettant au premier après l’avoir lancé.

Quelles sont les alternatives aux types bruts: utiliser des génériques

Un type brut est le nom d’une classe ou d’une interface générique sans aucun argument de type. Par exemple, compte tenu de la classe Box générique:

 public class Box { public void set(T t) { /* ... */ } // ... } 

Pour créer un type paramétré de Box , vous fournissez un argument de type réel pour le paramètre de type formel T :

 Box intBox = new Box<>(); 

Si l’argument de type réel est omis, vous créez un type brut de Box :

 Box rawBox = new Box(); 

Par conséquent, Box est le type brut du type générique Box . Cependant, une classe ou un type d’interface non générique n’est pas un type brut.

Les types bruts apparaissent dans le code hérité car de nombreuses classes d’API (telles que les classes Collections) n’étaient pas génériques avant JDK 5.0. Lorsque vous utilisez des types bruts, vous obtenez essentiellement un comportement pré-générique – une Box vous donne des Object s. Pour la compatibilité ascendante, l’atsortingbution d’un type paramétré à son type brut est autorisée:

 Box ssortingngBox = new Box<>(); Box rawBox = stringBox; // OK 

Mais si vous affectez un type brut à un type paramétré, vous obtenez un avertissement:

 Box rawBox = new Box(); // rawBox is a raw type of Box Box intBox = rawBox; // warning: unchecked conversion 

Vous obtenez également un avertissement si vous utilisez un type brut pour appeler des méthodes génériques définies dans le type générique correspondant:

 Box ssortingngBox = new Box<>(); Box rawBox = stringBox; rawBox.set(8); // warning: unchecked invocation to set(T) 

L’avertissement montre que les types bruts contournent les vérifications de type génériques, reportant la capture du code non sécurisé à l’exécution. Par conséquent, vous devriez éviter d’utiliser des types bruts.

La section Type Erasure contient plus d’informations sur la manière dont le compilateur Java utilise les types bruts.

Messages d’erreur non vérifiés

Comme mentionné précédemment, lors du mélange de code hérité avec du code générique, vous pouvez rencontrer des messages d’avertissement similaires aux suivants:

Remarque: Example.java utilise des opérations non contrôlées ou non sécurisées.

Remarque: recomstackr avec -Xlint: décoché pour plus de détails.

Cela peut se produire lors de l’utilisation d’une ancienne API fonctionnant sur des types bruts, comme illustré dans l’exemple suivant:

 public class WarningDemo { public static void main(Ssortingng[] args){ Box bi; bi = createBox(); } static Box createBox(){ return new Box(); } } 

Le terme “non vérifié” signifie que le compilateur ne dispose pas d’assez d’informations sur le type pour effectuer toutes les vérifications de type nécessaires à la sécurité du type. L’avertissement “non vérifié” est désactivé, par défaut, bien que le compilateur donne un indice. Pour voir tous les avertissements “non vérifiés”, recomstackz avec -Xlint: décoché.

Recomstackr l’exemple précédent avec -Xlint: non coché révèle les informations supplémentaires suivantes:

 WarningDemo.java:4: warning: [unchecked] unchecked conversion found : Box required: Box bi = createBox(); ^ 1 warning 

Pour désactiver complètement les avertissements non vérifiés, utilisez l’indicateur -Xlint: -unchecked. L’ @SuppressWarnings("unchecked") supprime les avertissements non vérifiés. Si vous ne connaissez pas la syntaxe @SuppressWarnings , voir Annotations.

Source originale: Tutoriels Java

  private static List list = new ArrayList(); 

Vous devez spécifier le paramètre type.

L’avertissement indique que les types définis pour prendre en charge les génériques doivent être paramétrés plutôt que d’utiliser leur forme brute.

List est définie pour prendre en charge les génériques: public class List . Cela permet de nombreuses opérations de type sécurisé, vérifiées à la compilation.

Un type “brut” dans Java est une classe qui n’est pas générique et qui traite des objects “bruts”, plutôt que des parameters de type génériques sûrs.

Par exemple, avant que Java Generics ne soit disponible, vous utiliseriez une classe de collection comme celle-ci:

 LinkedList list = new LinkedList(); list.add(new MyObject()); MyObject myObject = (MyObject)list.get(0); 

Lorsque vous ajoutez votre object à la liste, il ne se soucie pas du type d’object, et lorsque vous l’obtenez de la liste, vous devez explicitement le convertir au type attendu.

En utilisant des génériques, vous supprimez le facteur “inconnu”, car vous devez spécifier explicitement quel type d’object peut aller dans la liste:

 LinkedList list = new LinkedList(); list.add(new MyObject()); MyObject myObject = list.get(0); 

Notez qu’avec les génériques, vous n’avez pas besoin de lancer l’object provenant de l’appel get, la collection est prédéfinie pour fonctionner uniquement avec MyObject. Ce fait est le principal facteur déterminant pour les génériques. Il modifie une source d’erreurs d’exécution en quelque chose qui peut être vérifié au moment de la compilation.

Qu’est-ce qu’un type brut et pourquoi entends-je souvent qu’ils ne devraient pas être utilisés dans un nouveau code?

Un “type brut” est l’utilisation d’une classe générique sans spécifier d’argument de type pour ses types paramétrés, par exemple en utilisant List au lieu de List . Lorsque des génériques ont été introduits dans Java, plusieurs classes ont été mises à jour pour utiliser des génériques. L’utilisation de cette classe en tant que “type brut” (sans spécifier d’argument de type) permettait au code existant de toujours être compilé.

Les “types bruts” sont utilisés pour la rétrocompatibilité. Leur utilisation dans un nouveau code n’est pas recommandée car l’utilisation de la classe générique avec un argument de type permet un typage plus fort, ce qui peut améliorer la compréhension du code et entraîner des problèmes potentiels plus tôt.

Quelle est l’alternative si nous ne pouvons pas utiliser les types bruts, et comment ça se passe?

La solution préférée consiste à utiliser les classes génériques comme prévu – avec un argument de type approprié (par exemple, List ). Cela permet au programmeur de spécifier plus précisément les types, donne plus de sens aux futurs responsables concernant l’utilisation prévue d’une variable ou d’une structure de données, et permet au compilateur de renforcer la sécurité des types. Ces avantages combinés peuvent améliorer la qualité du code et empêcher l’introduction de certaines erreurs de codage.

Par exemple, pour une méthode où le programmeur veut s’assurer qu’une variable List appelée “names” ne contient que des chaînes:

 List names = new ArrayList(); names.add("John"); // OK names.add(new Integer(1)); // comstack error 

Le compilateur veut que vous écriviez ceci:

 private static List list = new ArrayList(); 

car sinon, vous pouvez append n’importe quel type que vous aimez dans la list , rendant l’instanciation comme new ArrayList() inutile. Les génériques Java sont uniquement une fonctionnalité à la compilation, donc un object créé avec un new ArrayList() acceptera avec JFrame éléments Integer ou JFrame s’il est affecté à une référence de la liste “raw type”. il est supposé contenir, seul le compilateur le fait.

Ici, je considère plusieurs cas à travers lesquels vous pouvez clarifier le concept

 1. ArrayList arr = new ArrayList(); 2. ArrayList arr = new ArrayList(); 3. ArrayList arr = new ArrayList(); 

Cas 1

ArrayList arr est une variable de référence ArrayList avec le type Ssortingng qui fait référence à un object ArralyList de type Ssortingng . Cela signifie qu’il ne peut contenir qu’un object de type chaîne.

C’est un Ssortingct à Ssortingng non un Raw, donc, il ne déclenchera jamais d’avertissement.

  arr.add("hello");// alone statement will comstack successfully and no warning. arr.add(23); //prone to comstack time error. //error: no suitable method found for add(int) 

Cas 2

Dans ce cas, ArrayList arr est un type ssortingct mais votre Object new ArrayList(); est un type brut.

  arr.add("hello"); //alone this comstack but raise the warning. arr.add(23); //again prone to comstack time error. //error: no suitable method found for add(int) 

ici arr est un type ssortingct. Donc, cela soulèvera une erreur de compilation lors de l’ajout d’un integer .

Attention : – Un object de type Raw est référencé à une variable de type Ssortingct référencée de type ArrayList .

Cas 3

Dans ce cas, ArrayList arr est un type brut, mais votre Object new ArrayList(); est un type ssortingct.

  arr.add("hello"); arr.add(23); //comstacks fine but raise the warning. 

Il y appenda tout type d’object car arr est un type brut.

Avertissement : – Un object de type Ssortingct est référencé à un type raw référencé variable.

Un type brut est l’absence d’un paramètre de type lors de l’utilisation d’un type générique.

Les caractères bruts ne doivent pas être utilisés car ils peuvent provoquer des erreurs d’exécution, comme l’insertion d’un double dans ce qui était censé être un Set de caractères int .

 Set set = new HashSet(); set.add(3.45); //ok 

Lorsque vous récupérez les éléments de l’ Set , vous ne savez pas ce qui sort. Supposons que vous vous attendiez à ce qu’il soit tout Integer , vous le lancez à Integer ; exception à l’exécution lorsque le double 3.45 arrive.

Avec un paramètre de type ajouté à votre Set , vous obtiendrez une erreur de compilation immédiatement. Cette erreur préventive vous permet de résoudre le problème avant que quelque chose ne saute pendant l’exécution (économisant ainsi du temps et des efforts).

 Set set = new HashSet(); set.add(3.45); //NOT ok. 

Ce qui est dit, c’est que votre list est une List d’objects non spécifiés. C’est-à-dire que Java ne sait pas quels types d’objects se trouvent dans la liste. Ensuite, lorsque vous souhaitez effectuer une itération de la liste, vous devez lancer chaque élément pour pouvoir accéder aux propriétés de cet élément (dans ce cas, Ssortingng).

En général, il est préférable de paramétrer les collections afin de ne pas avoir de problèmes de conversion, vous ne pourrez append que des éléments du type paramétré et votre éditeur vous proposera les méthodes de sélection appropriées.

 private static List list = new ArrayList(); 

page de tutoriel .

Un type brut est le nom d’une classe ou d’une interface générique sans aucun argument de type. Par exemple, compte tenu de la classe Box générique:

 public class Box { public void set(T t) { /* ... */ } // ... } 

Pour créer un type de boîte paramétré, vous devez fournir un argument de type réel pour le paramètre de type formel T:

 Box intBox = new Box<>(); 

Si l’argument de type réel est omis, vous créez un type brut de Box:

 Box rawBox = new Box(); 

Voici un autre cas où les types bruts vont vous mordre:

 public class StrangeClass { @SuppressWarnings("unchecked") public  X getSomethingElse() { return (X)"Testing something else!"; } public static void main(Ssortingng[] args) { final StrangeClass withGeneric = new StrangeClass<>(); final StrangeClass withoutGeneric = new StrangeClass(); final String value1, value2; // Works value1 = withGeneric.getSomethingElse(); // Produces compile error: // incompatible types: java.lang.Object cannot be converted to java.lang.String value2 = withoutGeneric.getSomethingElse(); } } 

Comme mentionné dans la réponse acceptée, vous perdez tout support pour les génériques dans le code du type brut. Chaque paramètre de type est converti en son effacement (qui dans l’exemple ci-dessus est juste Object ).

J’ai trouvé cette page après avoir fait quelques exercices et avoir exactement la même perplexité.

============== Je suis passé de ce code comme fourni par l’exemple ===============

 public static void main(Ssortingng[] args) throws IOException { Map wordMap = new HashMap(); if (args.length > 0) { for (int i = 0; i < args.length; i++) { countWord(wordMap, args[i]); } } else { getWordFrequency(System.in, wordMap); } for (Iterator i = wordMap.entrySet().iterator(); i.hasNext();) { Map.Entry entry = (Map.Entry) i.next(); System.out.println(entry.getKey() + " :\t" + entry.getValue()); } 

====================== À ce code ========================

 public static void main(Ssortingng[] args) throws IOException { // replace with TreeMap to get them sorted by name Map wordMap = new HashMap(); if (args.length > 0) { for (int i = 0; i < args.length; i++) { countWord(wordMap, args[i]); } } else { getWordFrequency(System.in, wordMap); } for (Iterator> i = wordMap.entrySet().iterator(); i.hasNext();) { Entry entry = i.next(); System.out.println(entry.getKey() + " :\t" + entry.getValue()); } } 

================================================== =============================

Cela peut être plus sûr, mais il a fallu 4 heures pour comprendre la philosophie ...

Les types bruts sont bien quand ils expriment ce que vous voulez exprimer.

Par exemple, une fonction de désérialisation peut renvoyer une List , mais elle ne connaît pas le type d’élément de la liste. So List est le type de retour approprié ici.