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>
où 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
Human
s à 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 Human
s 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.