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


Des questions:

  • Quels sont les types bruts dans Java, et pourquoi ai-je souvent entendu dire qu'ils ne devraient pas être utilisés dans le nouveau code?
  • Quelle est l'alternative si nous ne pouvons pas utiliser les types bruts, et comment est-ce mieux?

523
2018-05-05 02:48


origine


Réponses:


Qu'est-ce qu'un type brut?

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

JLS 4.8 Types bruts

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

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

  • Type de tableau dont le type d'élément est un type brut.

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

Voici un exemple à illustrer:

public class MyType<E> {
    class Inner { }
    static class Nested { }

    public static void main(String[] 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<Object> mt1; // no warning: type parameter given
        MyType<?> mt2;      // no warning: type parameter given (wildcard OK!)
    }
}

Ici, MyType<E> est un type paramétré (JLS 4.5). Il est commun de se référer familièrement à ce type simplement MyType pour faire court, mais techniquement, le nom est MyType<E>.

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

MyType.Nested n'est pas un type paramétré, même s'il s'agit d'un type de membre d'un type paramétré MyType<E>, parce que c'est static.

mt1, et mt2 sont tous deux déclarés avec des paramètres de type réels, ils 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 très bien, mais supposons que vous avez également les éléments suivants:

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

Maintenant, nous avons des problèmes à l'exécution, parce que names contient quelque chose qui n'est pas instanceof String.

Vraisemblablement, si vous voulez names ne contenir que String, toi pourrait peut-être encore utiliser un type brut et vérifier manuellement tous les  add vous-même, puis lancer manuellement à String chaque article de names. Encore mieux, mais ne doit pas utiliser un type brut et laissez le compilateur faire tout le travail pour vous, exploitant la puissance des génériques Java.

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

Bien sûr, si vous FAIRE vouloir names pour permettre un Boolean, alors vous pouvez le déclarer comme List<Object> names, et le code ci-dessus serait compiler.

Voir également


Comment un type brut différent de l'utilisation <Object> en tant que paramètres de type?

Ce qui suit est une citation de Effective Java 2nd Edition, Item 23: Ne pas utiliser les types bruts dans le nouveau code:

Juste quelle est la différence entre le type brut List et le type paramétré List<Object>? 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 objets de tout type. Alors que vous pouvez passer un List<String> à un paramètre de type List, vous ne pouvez pas le passer à un paramètre de type List<Object>. Il existe des règles de sous-typage pour les génériques, et List<String>est un sous-type du type brut List, mais pas du type paramétré List<Object>. En conséquence, vous perdez la sécurité de type si vous utilisez le type brut comme List, mais pas si vous utilisez un type paramétré comme List<Object>.

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

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

Les génériques en Java sont invariants. UNE List<String> n'est pas un List<Object>, ce qui suit générerait un avertissement de compilateur:

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

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

Voir également


Comment un type brut différent de l'utilisation <?> en tant que paramètre de type?

List<Object>, List<String>, etc sont tous List<?>, donc il peut être tentant de dire qu'ils sont juste List au lieu. Cependant, il y a une différence majeure: depuis List<E> définit seulement add(E), vous ne pouvez pas ajouter n'importe quel objet arbitraire à un List<?>. D'un autre côté, puisque le type brut List n'a pas de sécurité de type, vous pouvez add à peu près tout à un List.

Considérez la variante suivante de l'extrait précédent:

static void appendNewObject(List<?> list) {
    list.add(new Object()); // compilation error!
}
//...

List<String> names = new ArrayList<String>();
appendNewObject(names); // this part is fine!

Le compilateur a fait un travail merveilleux de vous protéger de potentiellement violer l'invariance de type de la List<?>! Si vous aviez déclaré le paramètre comme type brut List list, alors le code compilerait, et vous violeriez le type invariant de List<String> 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 quelconque des paramétrisations du type générique.

Le type d'un constructeur, méthode d'instance ou nonstatic champ d'un type brut C qui n'est pas héritée 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 lesstatic les champs sont également effacé.

Prenons l'exemple suivant:

class MyType<E> {
    List<String> getNames() {
        return Arrays.asList("John", "Mary");
    }

    public static void main(String[] args) {
        MyType rawType = new MyType();
        // unchecked warning!
        // required: List<String> found: List
        List<String> names = rawType.getNames();
        // compilation error!
        // incompatible types: Object cannot be converted to String
        for (String str : rawType.getNames())
            System.out.print(str);
    }
}

Lorsque nous utilisons le brut MyType, getNames devient effacé aussi, de sorte qu'il renvoie un brut List!

JLS 4.6 continue d'expliquer ce qui suit:

L'effacement de type mappe aussi la signature d'un constructeur ou d'une méthode à une signature qui n'a pas de type paramétré ou de type variable. 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 les effacements de tous les types de paramètres formels donnés dans s.

Le type de retour d'une méthode et les paramètres de type d'une méthode générique ou d'un constructeur subissent également un effacement si la signature de la méthode ou du constructeur est effacée.

L'effacement de la signature d'une méthode générique n'a pas de paramètres de type.

Le rapport de bogue suivant contient quelques réflexions de Maurizio Cimadamore, un développeur de compilateur, et Alex Buckley, l'un des auteurs de la JLS, sur les raisons de ce genre de comportement: https://bugs.openjdk.java.net/browse/JDK-6400189. (En bref, cela rend la spécification plus simple.)


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 existant. 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 efficace 2e édition a également ceci à ajouter:

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

La plate-forme Java était sur le point d'entrer dans sa deuxième décennie lorsque des 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é critique que tout ce code reste 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, conduit la décision de soutenir les types bruts.

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


N'y a-t-il aucune exception?

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

  • Littéraux de classe, par ex. List.class, ne pas List<String>.class
  • instanceof opérande, par ex. o instanceof Set, ne pas o instanceof Set<String>

Voir également


623
2018-05-05 04:50



Quels sont les types bruts dans Java, et pourquoi ai-je souvent entendu dire qu'ils ne devraient pas être utilisés dans le nouveau code?

Raw-types sont l'histoire ancienne du langage Java. Au début, il y avait Collections et ils ont tenu Objects Rien de plus et rien de moins. Chaque opération sur Collections moulages requis de Object au type désiré.

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

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

List aNumberList = new ArrayList();
String 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<String> aNumberList = new ArrayList<String>();
aNumberList.add("one");
Integer iOne = aNumberList.get(0);//Compile time error
String 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<String> aList = new ArrayList<String>(); //Contains only Strings

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<MyCompareAble>
{
   int id;
   public int compareTo(MyCompareAble other)
   {return this.id - other.id;}
}

Notez qu'il est impossible de mettre en œuvre CompareAble interface avec compareTo(MyCompareAble) avec des types bruts. Pourquoi vous ne devriez pas les utiliser:

  • Tout Object stocké dans un Collection doit être moulé avant de pouvoir être utilisé
  • Utiliser des génériques permet de compiler des vérifications de temps
  • L'utilisation de types bruts revient à stocker chaque valeur Object

Qu'est-ce que le compilateur fait: Les génériques sont rétrocompatibles, ils utilisent les mêmes classes Java que les types bruts. La magie arrive surtout au moment de la compilation.

List<String> someStrings = new ArrayList<String>();
someStrings.add("one");
String one = someStrings.get(0);

Sera compilé comme:

List someStrings = new ArrayList();
someStrings.add("one"); 
String one = (String)someStrings.get(0);

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

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


48
2018-05-05 21:50



Un type brut est le nom d'une classe ou d'une interface générique sans arguments de type. Par exemple, étant donné la classe Box générique:

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

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

Box<Integer> intBox = new Box<>();

Si l'argument de type réel est omis, vous créez un type brut de Box<T>:

Box rawBox = new Box();

Donc, Box est le type brut du type générique Box<T>. 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 - un Box vous donne Objects. Pour des raisons de compatibilité ascendante, l'attribution d'un type paramétré à son type brut est autorisée:

Box<String> stringBox = 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<T>
Box<Integer> intBox = rawBox;     // warning: unchecked conversion

Vous recevez é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<String> stringBox = new Box<>();
Box rawBox = stringBox;
rawBox.set(8);  // warning: unchecked invocation to set(T)

L'avertissement indique que les types bruts contournent les vérifications de type générique, en reportant la capture de 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 façon dont le compilateur Java utilise des types bruts.

Messages d'erreur non vérifiés

Comme mentionné précédemment, lorsque vous mélangez du code hérité avec du code générique, vous pouvez rencontrer des messages d'avertissement similaires à ceux-ci:

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

Remarque: recompiler avec -Xlint: non cochée pour plus de détails.

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

public class WarningDemo {
    public static void main(String[] args){
        Box<Integer> bi;
        bi = createBox();
    }

    static Box createBox(){
        return new Box();
    }
}

Le terme "non coché" signifie que le compilateur n'a pas assez d'informations de type pour effectuer toutes les vérifications de type nécessaires pour assurer la sécurité du type. L'avertissement "non coché" est désactivé, par défaut, bien que le compilateur donne un indice. Pour voir tous les avertissements "non cochés", recompilez avec -Xlint: non coché.

La recompilation de l'exemple précédent avec -Xlint: non cochée révèle les informations supplémentaires suivantes:

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

Pour désactiver complètement les avertissements non vérifiés, utilisez l'indicateur -Xlint: -unchecked. le @SuppressWarnings("unchecked") l'annotation supprime les avertissements non contrôlés. Si vous n'êtes pas familier avec le @SuppressWarnings syntaxe, voir Annotations.

Source primaire: Tutoriels Java 


21
2017-08-10 23:58



 private static List<String> list = new ArrayList<String>();

Vous devez spécifier le paramètre type.

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

List est défini pour soutenir les génériques: public class List<E>. Cela permet de nombreuses opérations sécurisées, qui sont vérifiées à la compilation.


16
2018-06-16 07:44



Un type "brut" dans Java est une classe qui n'est pas générique et qui traite des objets "bruts", plutôt que des paramètres de type génériques sûrs.

Par exemple, avant que les génériques Java soient disponibles, 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 objet à la liste, il ne se soucie pas du type d'objet, et lorsque vous l'extrayez de la liste, vous devez le convertir explicitement en type attendu.

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

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

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


12
2018-05-05 02:58



Qu'est-ce qu'un type brut et pourquoi ai-je souvent entendu dire 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 ex. en utilisant List au lieu de List<String>. Lorsque les 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 une typage plus fort, ce qui peut améliorer la compréhensibilité du code et conduire à la détection de problèmes potentiels plus tôt.

Quelle est l'alternative si nous ne pouvons pas utiliser les types bruts, et comment est-ce mieux?

L'alternative préférée est d'utiliser des classes génériques comme prévu - avec un argument de type approprié (par ex. List<String>). Cela permet au programmeur de spécifier des types plus spécifiquement, donne plus de sens aux futurs responsables de l'utilisation prévue d'une variable ou d'une structure de données, et permet au compilateur d'appliquer une meilleure sécurité de type. 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 de liste appelée «noms» ne contient que des chaînes:

List<String> names = new ArrayList<String>();
names.add("John");          // OK
names.add(new Integer(1));  // compile error

10
2018-05-05 04:31



Le compilateur veut que vous écriviez ceci:

private static List<String> list = new ArrayList<String>();

parce que sinon, vous pourriez ajouter n'importe quel type que vous aimez dans list, faisant l'instanciation comme new ArrayList<String>() inutile. Les génériques Java sont uniquement une fonctionnalité de compilation, donc un objet créé avec new ArrayList<String>() acceptera volontiers Integer ou JFrame éléments s'ils sont affectés à une référence du "type brut" List - L'objet lui-même ne sait rien sur les types qu'il est censé contenir, seul le compilateur le fait.


10
2018-06-16 07:53



Ici, je considère de multiples cas à travers lesquels vous pouvez clearifier le concept

1. ArrayList<String> arr = new ArrayList<String>();
2. ArrayList<String> arr = new ArrayList();
3. ArrayList arr = new ArrayList<String>();

Cas 1

ArrayList<String> arr c'est un ArrayList variable de référence avec le type String quelle référence à un ArralyList Objet de type String. Cela signifie qu'il ne peut contenir que des objets de type String.

C'est un Strict à String pas un type brut donc, il ne déclenchera jamais un avertissement.

    arr.add("hello");// alone statement will compile successfully and no warning.

    arr.add(23);  //prone to compile time error.
     //error: no suitable method found for add(int)

Cas 2

Dans ce cas ArrayList<String> arr est un type strict mais votre objet new ArrayList(); est un type brut.

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

ici arr est un type strict. Donc, il va augmenter l'erreur de compilation lors de l'ajout d'un integer.

Attention :- UNE Raw Type Object est référencé à un Strict type Référencé Variable de ArrayList.

Cas 3

Dans ce cas ArrayList arr est un type brut mais votre objet new ArrayList<String>(); est un type strict.

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

Il va ajouter n'importe quel type d'objet parce que arr est un type brut.

Attention :- UNE Strict Type Object est référencé à un raw type référencé Variable.


9
2018-05-13 13:51