Pourquoi est-List pas un sous-type de liste ?

StackOverflow https://stackoverflow.com/questions/1240316

Question

public void wahey(List<Object> list) {}

wahey(new LinkedList<Number>());

L'appel à la méthode ne sera pas de type vérification. Je ne peux même pas lancer le paramètre comme suit:

wahey((List<Object>) new LinkedList<Number>());

D'après mes recherches, je récolterez que la raison pour ne pas laisser ce type est la sécurité. Si nous avons été autorisés à faire ce qui précède, nous pourrions avoir les éléments suivants:

List<Double> ld;
wahey(ld);

Dans la méthode wahey, nous pourrions ajouter quelques cordes à la liste d'entrée (comme paramètre maintient une référence List<Object>). Maintenant, après l'appel de méthode, ld fait référence à une liste avec un List<Double> de type, mais la liste réelle contient des objets String!

Cela semble différent de la façon normale Java fonctionne sans génériques. Par exemple:

Object o;
Double d;
String s;

o = s;
d = (Double) o;

Ce que nous faisons ici est essentiellement la même chose, sauf cela passera des contrôles en temps compiler et n'échouer à l'exécution. La version avec les listes ne compilera pas.

Cela me conduit à croire que c'est purement une décision de conception en ce qui concerne le type de restrictions sur les génériques. J'espérais obtenir des commentaires sur cette décision?

Était-ce utile?

La solution

Qu'est-ce que vous faites dans l'exemple « sans génériques » est un casting, ce qui le rend clair que vous faites quelque chose de type dangereux. L'équivalent des génériques serait:

Object o;
List<Double> d;
String s;

o = s;
d.add((Double) o);

Ce qui se comporte de la même façon (compiles, mais échoue à l'exécution). La raison de ne pas autoriser le comportement que vous vous posez au sujet est parce qu'il permettrait implicites actions de type-dangereux, qui sont beaucoup plus difficiles à l'avis dans le code. Par exemple:

public void Foo(List<Object> list, Object obj) {
  list.add(obj);
}

Cela ressemble parfaitement bien et le type de sécurité jusqu'à ce que vous l'appelez comme ceci:

List<Double> list_d;
String s;

Foo(list_d, s);

Ce qui semble également le type de sécurité, parce que vous l'appelant ne savent pas nécessairement ce que Foo va faire avec ses paramètres.

Donc, dans ce cas, vous avez deux bits de code apparemment typées, qui finissent par être ensemble type dangereux. C'est mauvais, parce qu'il est caché et donc difficile à éviter et plus difficile à déboguer.

Autres conseils

Considérez si elle était ...

List<Integer> nums = new ArrayList<Integer>();
List<Object> objs = nums
objs.add("Oh no!");
int x = nums.get(0); //throws ClassCastException

Vous seriez en mesure d'ajouter quoi que ce soit du type parent à la liste, qui peut ne pas être ce qu'il était autrefois déclaré, qui, comme l'exemple ci-dessus montre, provoque toutes sortes de problèmes. Ainsi, il est interdit.

Ils ne sont pas des sous-types de l'autre en raison de la façon dont le travail génériques. Ce que vous voulez est de déclarer votre fonction comme ceci:

public void wahey(List<?> list) {}

Ensuite, il acceptera une liste de tout ce qui étend l'objet. Vous pouvez également faire:

public void wahey(List<? extends Number> list) {}

Cela vous permettra de prendre dans les listes de quelque chose qui est une sous-classe de Number.

Je vous recommande de choisir une copie de "Java Generics et Collections" par Maurice Naftalin et Philip Wadler.

Il existe essentiellement deux dimensions de l'abstraction ici, l'abstraction de la liste et l'abstraction de son contenu. Il est tout à varier le long de l'abstraction de la liste - dire, par exemple, que c'est un LinkedList ou un ArrayList - mais il ne va pas bien de limiter davantage le contenu, dire: Cette (liste qui contient des objets) est une (liste chaînée qui détient des chiffres). Parce que toute référence qu'il connaît comme (liste qui contient des objets) comprend, par le contrat de ce type, qu'il peut contenir any objet.

Ceci est tout à fait différent de ce que vous avez fait dans le code exemple non génériques, où vous avez dit: traiter cette chaîne comme si elle était un double. Toi, tu es en train de dire: traiter cette (liste qui contient uniquement des chiffres) comme (liste qui détient quoi que ce soit). Et il ne fonctionne pas et le compilateur peut le détecter, il ne vous laisse pas sortir avec elle.

  

"Ce que nous faisons ici est essentiellement   la même chose, sauf cela passera   la compilation des contrôles et seulement échouer à   la gestion du temps. La version avec les listes ne seront pas   compiler. "

Qu'est-ce que vous observez est parfaitement logique si l'on considère que l'objectif principal des génériques Java est d'obtenir des incompatibilités de type échouer au moment de la compilation au lieu de l'exécution.

De java.sun.com

  

Génériques fournit un moyen pour vous   communiquer le type d'une collection   au compilateur, afin qu'il puisse être   vérifié. Une fois que le compilateur connaît le   type d'élément de la collection, la   compilateur peut vérifier que vous avez utilisé   la collection cohérente et peut   insérer les moulages sur les valeurs correctes   étant retirés de la collection.

En Java, List<S> n'est pas un sous-type de List<T> quand S est un sous-type de T. Cette règle assure la sécurité de type.

Disons que nous permettons à un List<String> d'être un sous-type de List<Object>. Prenons l'exemple suivant:

public void foo(List<Object> objects) {
    objects.add(new Integer(42));
}

List<String> strings = new ArrayList<String>();
strings.add("my string");
foo(strings); // this is not allow in java
// now strings has a string and an integer!
// what would happen if we do the following...??
String myString = strings.get(1);

Alors, forçant cette offre de type sécurité, mais il a aussi un inconvénient, il est moins souple. Prenons l'exemple suivant:

class MyCollection<T> {
    public void addAll(Collection<T> otherCollection) {
        ...
    }
}

Ici vous avez une collection de années T, vous voulez ajouter tous les éléments d'une autre collection. Vous ne pouvez pas appeler cette méthode avec un Collection<S> pour un sous-type de S de T. Idéalement, cela est ok parce que vous ajoutez seulement des éléments dans votre collection, vous ne modifiez pas la collection de paramètres.

Pour résoudre ce problème, Java fournit ce qu'ils appellent « wildcards ». Les jokers sont un moyen de fournir covariance / contravariance. Considérons maintenant le suivant à l'aide des caractères génériques:

class MyCollection<T> {
     // Now we allow all types S that are a subtype of T
     public void addAll(Collection<? extends T> otherCollection) {
         ...

         otherCollection.add(new S()); // ERROR! not allowed (Here S is a subtype of T)
     }
} 

Maintenant, avec des jokers nous permettons covariance dans le type T et nous bloquons les opérations qui ne sont pas SÛR type (par exemple l'ajout d'un élément dans la collection). De cette façon, nous obtenons la flexibilité et la sécurité de type.

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top