Sostituire i metodi passando come argomento l'oggetto sottoclasse in cui è previsto il supertipo


12

Sto solo imparando Java e non sono un programmatore praticante.

Il libro che sto seguendo dice che quando si sovrascrive un metodo, i tipi di argomento devono essere gli stessi, ma i tipi di ritorno possono essere polimorficamente compatibili.

La mia domanda è: perché gli argomenti passati al metodo di sostituzione non possono essere un tipo di sottoclasse del super-tipo previsto?

Nel metodo sovraccarico, qualsiasi metodo che io chiamo sull'oggetto è garantito per essere definito sull'oggetto.


Note sui duplicati suggeriti:

Il primo suggerimento sembra riguardare la gerarchia di classi e dove mettere la funzionalità. La mia domanda è più focalizzata sul perché esiste la restrizione linguistica.

Il secondo suggerimento spiega come fare ciò che sto chiedendo, ma non perché debba essere fatto in questo modo. La mia domanda è focalizzata sul perché.


1
chiesto e risposto nella domanda precedente : "Questo è generalmente considerato come un fallimento del principio di sostituzione di Liskov (LSP), perché il vincolo aggiunto rende le operazioni della classe base non sempre appropriate per la classe derivata ..."
gnat


@gnat la domanda non è se richiedere sottotipi più specifici per argomenti violi qualche principio del computer, la domanda è se è possibile, a cui edalorzo ha risposto.
user949300,

Risposte:


18

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 Tappaia possiamo passare un S.

Come tale, è sicuro restituire un Squando 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 AnimalHunterdovresti essere in grado di cacciare qualsiasi animale, ma secondo la tua implementazione MammutHunteraccetti solo Mammutoggetti. 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 MammalHunterstrumenti 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.


3

Il problema non è quello che fai nel metodo di sostituzione con l'oggetto indicato come argomento. Il problema è qual è il tipo di argomenti che il codice che utilizza il tuo metodo è autorizzato a fornire al tuo metodo.

Un metodo di sostituzione deve soddisfare il contratto del metodo di sostituzione. Se il metodo sovrascritto accetta argomenti di qualche classe e lo sostituisci con un metodo che accetta solo una sottoclasse, non soddisfa il contratto. Ecco perché non è valido.

Sovrascrivere un metodo con un tipo più specifico di argomento si chiama sovraccarico . Definisce un metodo con lo stesso nome ma con un tipo di argomento diverso o più specifico. È disponibile un metodo sovraccarico oltre al metodo originale. Quale metodo viene chiamato dipende dal tipo noto al momento della compilazione. Per sovraccaricare un metodo devi eliminare l'annotazione @Override.

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.