Facciamo un esempio con una classe di base non astratta:
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) {
// ...
}
}
in qualsiasi luogo in cui ci si aspetta un Human
, un Student
o Teacher
farà altrettanto bene, come attuare pienamente l'interfaccia Human
. (In questo caso, è possibile chiamare tale getName()
.) L'ereditarietà di Java garantisce che questo sia il caso tecnicamente. Rendendolo funzionare semanticamente è il lavoro dell'autore della classe, in modo che il suo codice soddisfi lo Liskov substitution principle.
Quindi, questo non significa che possiamo anche sostituire Collection<Teacher>
dove è previsto uno Collection<Human>
? Non sempre. Si consideri il seguente metodo:
public class Human {
// ...
public void join(Set<Human> party) {
party.add(this);
}
}
Ora, se Java ha permesso un Set<Student>
per essere passato come partito, qualsiasi tentativo di non Student
Human
s per unire quel partito avrebbe dovuto fallire in fase di esecuzione.
Come regola generale, un contenitore di un sottotipo non è adatto se il ricevitore (callee in caso di un argomento di una funzione, chiamata in caso di un valore di ritorno funzione) vuole mettere qualcosa in esso, ma accettabile se la il ricevitore vuole solo estrarre materiale e usarlo. Un contenitore di un supertipo non è adatto se il destinatario desidera estrarre e utilizzare il materiale, ma è accettabile se il ricevitore inserisce sempre solo materiale. Di conseguenza, se il ricevente preleva entrambi elementi dalla raccolta e inserisce elementi nella raccolta, di solito devono richiedere una raccolta di un tipo fisso.
Il nostro metodo join
mette solo Human
s nel party
, così abbiamo potuto anche permettere un Set<Object>
o non generico Set
o equivalentemente un Set<?>
. Java ci permette di farlo con lower-bounded wildcards:
public class Human {
// ...
public void join(Set<? super Human> party) {
party.add(this);
}
}
per l'apertura alle possibilità verso sottoclassi, c'è upper-bounded wildcards:
public class Teacher extends Human {
public void teach(Subject subject, Set<? extends Student> schoolClass) {
for (Student student : class) {
student.learn(subject);
}
}
}
Ora, se mai sottoclasse Student
, il passato schoolClass
può essere un Set
di anche quel sottotipo.
Dai un'occhiata a questo articolo sul perché Java [i generici non sono covarianti] (http://www.ibm.com/developerworks/library/j-jtp01255/). – Ralf
'Elenco estende il numero> 'funzionerebbe. Previene 'add'. –