Ich lerne gerade Java und bin kein praktizierender Programmierer.
Das Buch, dem ich folge, besagt, dass beim Überschreiben einer Methode die Argumenttypen identisch sein müssen, die Rückgabetypen jedoch polymorph kompatibel sein können.
Meine Frage ist, warum die Argumente, die an die überschreibende Methode übergeben werden, keine Unterklassen des erwarteten Supertyps sein können.
In der überladenen Methode wird garantiert, welche Methode ich für das Objekt aufrufe, für das Objekt definiert.
Anmerkungen zu vorgeschlagenen Duplikaten:
Der erste Vorschlag scheint sich mit der Klassenhierarchie und dem Ort zu befassen, an dem die Funktionalität bereitgestellt werden soll. Meine Frage konzentriert sich eher darauf, warum die Spracheinschränkung besteht.
Der zweite Vorschlag erklärt, wie man das macht, wonach ich frage, aber nicht, warum es so gemacht werden muss. Meine Frage konzentriert sich auf das Warum.
quelle
Antworten:
Das Konzept, auf das Sie sich in Ihrer Frage zunächst beziehen, heißt kovariante Rückgabetypen .
Covariante Rückgabetypen funktionieren, weil eine Methode ein Objekt eines bestimmten Typs zurückgeben soll und überschreibende Methoden möglicherweise tatsächlich eine Unterklasse davon zurückgeben. Basierend auf den Subtypisierungsregeln einer Sprache wie Java können wir , wenn
S
es sich um einen Subtyp handelt, eine übergebenT
, wo immer diesT
auftrittS
.Als solches ist es sicher, eine zurückzugeben,
S
wenn eine Methode überschrieben wird, die a erwartetT
.Ihr Vorschlag, zu akzeptieren, dass beim Überschreiben einer Methode Argumente verwendet werden, die Untertypen der von der überschriebenen Methode angeforderten sind, ist viel komplizierter, da dies zu Unklarheiten im Typsystem führt.
Auf der einen Seite funktioniert es nach den oben genannten Subtypisierungsregeln höchstwahrscheinlich bereits für das, was Sie tun möchten. Zum Beispiel
Nichts hindert Implementierungen dieser Klasse daran, irgendeine Art von Tier zu empfangen, da dies bereits die Kriterien in Ihrer Frage erfüllt.
Nehmen wir jedoch an, wir könnten diese Methode überschreiben, wie Sie vorgeschlagen haben:
Hier ist der lustige Teil, jetzt können Sie dies tun:
Gemäß der öffentlichen Schnittstelle von
AnimalHunter
sollten Sie in der Lage sein, jedes Tier zu jagen, aber gemäß Ihrer ImplementierungMammutHunter
akzeptieren Sie nurMammut
Objekte. Daher erfüllt die Überschreibungsmethode die öffentliche Schnittstelle nicht. Wir haben hier nur die Solidität des Typsystems gebrochen.Sie können das implementieren, was Sie möchten, indem Sie Generika verwenden.
Dann könnten Sie Ihren MammutHunter definieren
Und mit generischer Kovarianz und Kontravarianz können Sie die Regeln bei Bedarf zu Ihren Gunsten lockern. Zum Beispiel könnten wir sicherstellen, dass ein Säugetierjäger nur in einem bestimmten Kontext Katzen jagen kann:
Angenommen
MammalHunter
, AnbaugeräteAnimalHunter<Mammal>
.In diesem Fall würde dies nicht akzeptiert:
Selbst wenn es sich bei Mammuts um Säugetiere handelt, würde dies aufgrund der Beschränkungen des hier verwendeten kontravarianten Typs nicht akzeptiert. Sie können also immer noch eine gewisse Kontrolle über die Typen ausüben, um Dinge wie die von Ihnen erwähnten zu tun.
quelle
Das Problem ist nicht das, was Sie in der überschreibenden Methode mit dem als Argument angegebenen Objekt tun. Das Problem ist, welche Art von Argumenten der Code, der Ihre Methode verwendet, in Ihre Methode einspeisen darf.
Eine überschreibende Methode muss den Vertrag der überschriebenen Methode erfüllen. Wenn die überschriebene Methode Argumente einer Klasse akzeptiert und Sie sie mit einer Methode überschreiben, die nur eine Unterklasse akzeptiert, erfüllt sie den Vertrag nicht. Deshalb ist es nicht gültig.
Das Überschreiben einer Methode mit einem spezifischeren Argumenttyp wird als Überladen bezeichnet . Es definiert eine Methode mit demselben Namen, jedoch mit einem anderen oder spezifischeren Argumenttyp. Neben der ursprünglichen Methode steht eine überladene Methode zur Verfügung. Welche Methode aufgerufen wird, hängt vom Typ ab, der zum Zeitpunkt der Kompilierung bekannt ist. Um eine Methode zu überladen, müssen Sie die @ Override-Annotation löschen.
quelle