30 votes

Quel type de sécurité aurait perdu si Generics avait pris en charge le sous-typage?

Considérons l'extrait:

Number[] numbers = {1, 2.3, 4.5f, 6000000000000000000L};

Il est parfaitement normal de le faire ci-dessus, Number est une classe abstraite.

Aller de l'avant,

List<Long> listLong = new ArrayList<Long>();
listLong.add(Long.valueOf(10));

List<Number> listNumbers = listLong; // compiler error    - LINE 3

listNumbers.add(Double.valueOf(1.23));

A la Ligne 3 a été conçu pour être compilé correctement, on allait se retrouver avec un List de Numbers, je.e,

for(Number num: listNumbers ){
    System.out.println(num);
}

// 10
// 1.23

qui sont tous les nombres.

Je suis tombé sur un bouquin,

Les génériques ne prend pas en charge le sous-typage parce que cela va causer des problèmes dans la réalisation de sécurité de type. C'est pourquoi, List<T> n'est pas considérée comme une sous-type de l' List<S>S est le super-type d' T

Quel type de sécurité aurait perdu dans cette spécifique cas comme discuté ci-dessus, ont été la Ligne 3 a été à compiler avec succès?

42voto

JB Nizet Points 250258
 List<Long> listLong = new ArrayList<Long>();
List<Number> listNumbers = listLong;
 

Ainsi, listNumbers et listLong seraient deux références à la même liste, si cela était possible, non?

 listNumbers.add(Double.valueOf(1.23));
 

Vous pourrez donc ajouter un double à cette liste. listLong , de type List<Long> , contiendrait donc un double. La sécurité de type serait donc brisée.

8voto

Maroun Maroun Points 31217

Si c'était le cas, alors nous pourrions ajouter d'autres sous-types différents de Number en listNumbers, ce qui doit être interdit.

Imaginez que vous êtes en train d'insérer des objets de type Double et Long, et plus tard, vous essayez d'utiliser Long#reverse. Votre code sera compiler mais, bien sûr, ne seront pas en mesure d'exécution (mauvaise), le premier Double qu'il va venir à travers.

5voto

Borun Points 21

Prenons un exemple avec un non-classe de base abstraite:

public class Human {
    public string getName() {
        // ...
    }
}

public class Student extends Human {
    public void learn(Subject subject) {
        // ...
    }
}

public class Teacher extends Human {
    public void teach(Subject subject) {
        // ...
    }
}

En tout lieu où un Human est prévu, un Student ou Teacher sera tout aussi bien, comme ils mettre pleinement en œuvre l' Human interface. (Dans ce cas, que getName() peut être appelé sur eux). Java héritage garantit que c'est le cas sur le plan technique. Mettre en œuvre du point de vue sémantique, c'est la classe de l'auteur de l'emploi, de sorte que son code remplit le principe de substitution de Liskov.

N'est pas ce à dire qu'on peut aussi le remplacer par Collection<Teacher>Collection<Human> est prévu? Pas toujours. Considérons la méthode suivante:

public class Human {
    // ...

    public void join(Set<Human> party) {
        party.add(this);
    }
}

Maintenant, si Java a permis une Set<Student> à être passé sous la partie, toutes les tentatives de non-Student Humans à se joindre à cette partie aurait à l'échec lors de l'exécution.

En règle générale, un conteneur d'un sous-type est impropre si le récepteur (destinataire de l'appel dans le cas d'une fonction à l'argument de l'appelant, dans le cas d'une fonction de valeur de retour) veut mettre quelque chose en elle, mais acceptable si le récepteur ne veulent prendre des choses et de l'utiliser. Un conteneur d'un supertype est impropre si le récepteur veut prendre des trucs et de l'utiliser, mais acceptable si le récepteur ne met jamais de trucs dedans. En conséquence, si le récepteur prend des choses de la collecte et met des choses dans la collection, ils doivent généralement exiger une collection de type fixe.

Notre join méthode ne met Humans en party, donc on pourrait également permettre à un Set<Object> ou un non-générique Set ou, de manière équivalente, un Set<?>. Java nous permet de le faire avec la plus faible délimitées par des caractères génériques:

public class Human {
    // ...

    public void join(Set<? super Human> party) {
        party.add(this);
    }
}

Pour l'ouverture, les possibilités vers les sous-classes, il y a supérieure délimitées par des caractères génériques:

public class Teacher extends Human {
    public void teach(Subject subject, Set<? extends Student> schoolClass) {
        for (Student student : class) {
            student.learn(subject);
        }
    }
}

Maintenant, si jamais nous sous-classe Student, l' schoolClass peut être un Set de ce sous-type, trop.

3voto

Paul Draper Points 14352

Le concept vous faites référence est la variance.

En d'autres termes, si S est un supertype de T, est - List<S> d'un sous-type, supertype, de l'égalité de type, ou unreleted d' List<T>?

La réponse pour List - et de toutes les autres Java génériques* -- est "non reliés", c'est à dire invariant.

class SuperType {}

class Type extends SuperType {}

class SubType extends Type {}

List<Type> list = ...

List<SuperType> superList = list;
superList.add(new SuperType());
// no, we shouldn't be able to add a SuperType to list

List<SubType> subList = list;
SubType item = subList.get(0);
// no, there's not necessarily only SubType items in list

*Java n' ont la notion "d'utilisation" de la variance, avec des caractères génériques (?). Cela permettra de limiter quelles sont les méthodes possible de faire appel.

List<Type> list = ...

List<? super SubType> wildcardList = list;
wildcardList.add(new SubType());
// but...everything we get() is an Object

ou

List<Type> list = ...

List<? extends SuperType> wildcardList = list;
SuperType item = wildcard.get(0);
// but...it's impossible to add()

Pour info, certaines langues ont la notion de définition-site de la variance, par exemple Scala. Donc, List[Int] est en effet un sous-type d' List[Number]. C'est possible avec immuables collections (encore une fois, un ensemble limité de méthodes), mais évidemment pas pour les mutable ceux.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X