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

10

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 uguali, ma i tipi di ritorno possono essere polimorficamente compatibili.

La mia domanda è perché non è possibile che gli argomenti passati al metodo di sovrascrittura non siano un tipo sottoclasse del super-tipo previsto?

Nel metodo sovraccarico, qualunque metodo che invoco sull'oggetto è garantito per essere definito sull'oggetto.

Note sui duplicati suggeriti:

Il primo suggerimento sembra riguardare la gerarchia delle classi e dove mettere 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é deve essere fatto in questo modo. La mia domanda è incentrata sul perché.

    
posta user1720897 24.12.2014 - 16:55
fonte

2 risposte

18

Il concetto a cui inizialmente fai riferimento nella tua domanda è chiamato tipi di ritorno covarianti .

I tipi di restituzione covarianti funzionano perché si suppone che un metodo restituisca un oggetto di un certo tipo e che i metodi di sovrascrittura possano effettivamente restituire una sottoclasse di esso. In base alle regole di sottotipizzazione di un linguaggio come Java, se S è un sottotipo di T , allora ovunque sia visualizzato T possiamo passare un S .

In questo modo è possibile restituire un S quando si esegue l'override di un metodo che prevede un T .

Il tuo suggerimento di accettare che un metodo che sovrascrive usi argomenti che sono sottotipi di quelli richiesti dal metodo sovrascritto è molto più complicato in quanto porta a una mancanza nel sistema dei tipi.

Da una parte, con le stesse regole di sottotitoli sopra menzionate, molto probabilmente funziona già per quello che vuoi fare. Ad esempio

interface Hunter {
   public void hunt(Animal animal);
}

Niente impedisce alle implementazioni di questa classe di ricevere qualsiasi tipo di animale, in quanto tale soddisfa già i criteri nella tua domanda.

Ma supponiamo di poter sovrascrivere questo metodo come suggerito:

class MammutHunter implements Hunter {
  @Override
  public void hunt(Mammut animal) {
  }
}

Ecco la parte divertente, ora puoi fare questo:

AnimalHunter hunter = new MammutHunter();
hunter.hunt(new Bear()); //Uh oh

Come per l'interfaccia pubblica di AnimalHunter dovresti essere in grado di cacciare qualsiasi animale, ma come per la tua implementazione di MammutHunter accetti solo Mammut oggetti. Pertanto il metodo sovrascritto 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 il tuo MammutHunter

class MammutHunter implements AnimalHunter<Mammut> {
   void hunt(Mammut m){
   }
}

E usando la covarianza e la contravarianza generiche puoi rilassare le regole a tuo favore quando necessario. Ad esempio, potremmo assicurarci che un cacciatore di mammiferi possa cacciare solo felini in un dato contesto:

AnimalHunter<? super Feline> hunter = new MammalHunter();
hunter.hunt(new Lion());
hunter.hunt(new Puma());

Supponendo che MammalHunter implementa AnimalHunter<Mammal> .

In tal caso ciò non sarebbe stato accettato:

hunter.hunt(new Mammut()):

Anche se i mammut sono mammiferi, non sarebbero accettati a causa delle restrizioni sul tipo controvariante che stiamo usando qui. Quindi, puoi ancora esercitare un controllo sui tipi per fare cose come quelle che hai citato.

    
risposta data 24.12.2014 - 21:37
fonte
3

Il problema non è quello che fai nel metodo di sovrascrittura con l'oggetto dato come argomento. Il problema è qual è il tipo di argomenti che il codice che utilizza il tuo metodo può alimentare con il tuo metodo.

Un metodo prevalente deve soddisfare il contratto del metodo sottoposto a override. Se il metodo sottoposto a override accetta argomenti di qualche classe e l'hai sovrascritto con un metodo che accetta solo una sottoclasse, non soddisfa il contratto. Questo è il motivo per cui non è valido.

Sostituire un metodo con un tipo di argomento più specifico è chiamato overloading . Definisce un metodo con lo stesso nome ma con un tipo di argomento diverso o più specifico. Un metodo sovraccarico è disponibile oltre al metodo originale. Il metodo chiamato dipende dal tipo noto al momento della compilazione. Per sovraccaricare un metodo devi eliminare l'annotazione @ Override.

    
risposta data 26.12.2014 - 17:57
fonte

Leggi altre domande sui tag