Il concetto a cui inizialmente fai riferimento nella tua domanda si chiama tipi di ritorno covarianti .
I tipi di ritorno covarianti funzionano perché si suppone che un metodo restituisca un oggetto di un certo tipo e i metodi di sostituzione possono effettivamente restituirne una sottoclasse. Basato sulle regole del sottotipo di un linguaggio come Java, se S
è un sottotipo di T
, allora dovunque T
appaia possiamo passare un S
.
Come tale, è sicuro restituire un S
quando si ignora un metodo che prevede un T
.
Il tuo suggerimento di accettare che un metodo di sostituzione utilizzi argomenti che sono sottotipi di quelli richiesti dal metodo di sostituzione è molto più complicato poiché porta all'instabilità nel sistema dei tipi.
Da un lato, con le stesse regole di sottotipizzazione sopra menzionate, molto probabilmente funziona già per quello che vuoi fare. Per esempio
interface Hunter {
public void hunt(Animal animal);
}
Nulla impedisce alle implementazioni di questa classe di ricevere alcun tipo di animale, in quanto tale soddisfa già i criteri della tua domanda.
Supponiamo che potremmo ignorare questo metodo come hai suggerito:
class MammutHunter implements Hunter {
@Override
public void hunt(Mammut animal) {
}
}
Ecco la parte divertente, ora puoi farlo:
AnimalHunter hunter = new MammutHunter();
hunter.hunt(new Bear()); //Uh oh
Secondo la tua interfaccia pubblica AnimalHunter
dovresti essere in grado di cacciare qualsiasi animale, ma secondo la tua implementazione MammutHunter
accetti solo Mammut
oggetti. Pertanto, il metodo overriden non soddisfa l'interfaccia pubblica. Abbiamo appena rotto la solidità del sistema di tipi qui.
Puoi implementare ciò che vuoi usando i generici.
interface AnimalHunter<T extends Animal> {
void hunt(T animal);
}
Quindi potresti definire MammutHunter
class MammutHunter implements AnimalHunter<Mammut> {
void hunt(Mammut m){
}
}
E usando la covarianza e la contraddizione generiche puoi allentare le regole a tuo favore quando necessario. Ad esempio, potremmo assicurarci che un cacciatore di mammiferi possa cacciare i felini solo in un determinato contesto:
AnimalHunter<? super Feline> hunter = new MammalHunter();
hunter.hunt(new Lion());
hunter.hunt(new Puma());
Supponiamo MammalHunter
strumenti AnimalHunter<Mammal>
.
In tal caso questo non sarebbe accettato:
hunter.hunt(new Mammut()):
Anche quando i mammut sono mammiferi, questo non sarebbe accettato a causa delle restrizioni sul tipo di contravarianza che stiamo usando qui. Quindi, puoi ancora esercitare un po 'di controllo sui tipi per fare cose come quelle che hai menzionato.