Was sind gute Gründe, um die Vererbung in Java zu verbieten, beispielsweise durch die Verwendung von Abschlussklassen oder Klassen mit einem einzigen privaten, parameterlosen Konstruktor? Was sind gute Gründe, um eine Methode endgültig zu machen?
Was sind gute Gründe, um die Vererbung in Java zu verbieten, beispielsweise durch die Verwendung von Abschlussklassen oder Klassen mit einem einzigen privaten, parameterlosen Konstruktor? Was sind gute Gründe, um eine Methode endgültig zu machen?
Antworten:
Ihre beste Referenz hier ist Punkt 19 von Joshua Blochs ausgezeichnetem Buch "Effective Java" mit dem Titel "Design und Dokument zur Vererbung oder zum Verbot". (Es ist Punkt 17 in der zweiten Ausgabe und Punkt 15 in der ersten Ausgabe.) Sie sollten es wirklich lesen, aber ich werde es zusammenfassen.
Die Interaktion geerbter Klassen mit ihren Eltern kann überraschend und unvorhersehbar sein, wenn der Vorfahr nicht für die Vererbung konzipiert wurde.
Klassen sollten daher in zwei Arten kommen:
Klassen , die erweitert werden sollen und über genügend Dokumentation verfügen, um zu beschreiben, wie dies durchgeführt werden soll
Klassen als endgültig markiert
Wenn Sie rein internen Code schreiben, kann dies ein wenig übertrieben sein. Der zusätzliche Aufwand beim Hinzufügen von fünf Zeichen zu einer Klassendatei ist jedoch sehr gering. Wenn Sie nur für den internen Verbrauch schreiben, kann ein zukünftiger Codierer immer das 'Finale' entfernen - Sie können es sich als Warnung vorstellen, dass "diese Klasse nicht unter Berücksichtigung der Vererbung entworfen wurde".
Möglicherweise möchten Sie eine Methode endgültig festlegen, damit überschreibende Klassen das Verhalten nicht ändern können, auf das in anderen Methoden gerechnet wird. In Konstruktoren aufgerufene Methoden werden häufig als endgültig deklariert, damit Sie beim Erstellen von Objekten keine unangenehmen Überraschungen erleben.
final
.
Ein Grund für ein Klassenfinale wäre, wenn Sie die Komposition der Vererbung aufzwingen möchten. Dies ist im Allgemeinen wünschenswert, um eine enge Kopplung zwischen Klassen zu vermeiden.
Es gibt 3 Anwendungsfälle, in denen Sie endgültige Methoden anwenden können.
Zweck für ein Klassenfinale:
Damit kein Körper diese Klassen erweitern und ihr Verhalten ändern kann.
Beispiel: Wrapper-Klasse Integer ist eine letzte Klasse. Wenn diese Klasse nicht endgültig ist, kann jeder Integer in seine eigene Klasse erweitern und das Grundverhalten der Integer-Klasse ändern. Um dies zu vermeiden, hat Java alle Wrapper-Klassen als endgültige Klassen erstellt.
DerivedInteger
Die ursprüngliche Integer-Klasse wird immer noch nicht geändert. Wer sie verwendet, DerivedInteger
tut dies auf eigenes Risiko. Daher verstehe ich immer noch nicht, warum dies ein Problem ist.
Sie möchten vielleicht unveränderliche Objekte (machen http://en.wikipedia.org/wiki/Immutable_object ), könnten Sie einen Singleton erstellen möchten ( http://en.wikipedia.org/wiki/Singleton_pattern ), oder möchten Sie vielleicht um zu verhindern, dass jemand die Methode aus Gründen der Effizienz, Sicherheit oder Sicherheit überschreibt.
Vererbung ist wie eine Kettensäge - sehr mächtig, aber schrecklich in den falschen Händen. Entweder entwerfen Sie eine Klasse, von der geerbt werden soll (was die Flexibilität einschränken und viel länger dauern kann), oder Sie sollten sie verbieten.
Siehe Effective Java 2nd Edition, Artikel 16 und 17, oder meinen Blog-Beitrag "Inheritance Tax" .
Hmmm ... ich kann mir zwei Dinge vorstellen:
Möglicherweise haben Sie eine Klasse, die sich mit bestimmten Sicherheitsproblemen befasst. Ein Angreifer kann Sicherheitsbeschränkungen umgehen, indem er es unterordnet und Ihrem System die untergeordnete Version davon zuführt. Beispielsweise unterstützt Ihre Anwendung möglicherweise Plugins, und wenn ein Plugin nur Ihre sicherheitsrelevanten Klassen unterordnen kann, kann es diesen Trick verwenden, um eine untergeordnete Version davon irgendwie an seinen Platz zu schmuggeln. Dies ist jedoch eher etwas, mit dem sich Sun in Bezug auf Applets und dergleichen befassen muss, möglicherweise kein so realistischer Fall.
Viel realistischer ist es, zu vermeiden, dass ein Objekt veränderlich wird. Da Strings beispielsweise unveränderlich sind, kann Ihr Code sicher Verweise darauf behalten
String blah = someOtherString;
anstatt zuerst die Zeichenfolge zu kopieren. Wenn Sie jedoch eine Zeichenfolge unterordnen können, können Sie Methoden hinzufügen, mit denen der Zeichenfolgenwert geändert werden kann. Jetzt kann sich kein Code mehr darauf verlassen, dass die Zeichenfolge dieselbe bleibt, wenn sie nur die Zeichenfolge wie oben kopiert, sondern die Zeichenfolge dupliziert Zeichenfolge.
Wenn Sie eine kommerzielle Closed-Source-Klasse schreiben, möchten Sie möglicherweise nicht, dass die Benutzer die Funktionalität später ändern können, insbesondere wenn Sie Unterstützung dafür benötigen und die Benutzer Ihre Methode überschrieben haben und sich über den Aufruf beschweren unerwartete Ergebnisse.
Wenn Sie Klassen und Methoden als endgültig markieren, stellen Sie möglicherweise einen kleinen Leistungsgewinn fest, da die Laufzeit nicht nach der richtigen Klassenmethode suchen muss, um sie für ein bestimmtes Objekt aufzurufen. Nicht endgültige Methoden werden als virtuell markiert, damit sie bei Bedarf ordnungsgemäß erweitert werden können. Endliche Methoden können direkt in der Klasse verknüpft oder inline kompiliert werden.
Menschen davon abzuhalten, Dinge zu tun, die sich und andere verwirren könnten. Stellen Sie sich eine Physikbibliothek vor, in der Sie einige definierte Konstanten oder Berechnungen haben. Ohne das letzte Schlüsselwort könnte jemand mitkommen und grundlegende Berechnungen oder Konstanten neu definieren, die sich NIEMALS ändern sollten.
Sie möchten eine Methode endgültig machen, damit das Überschreiben von Klassen ihr Verhalten nicht ändert. Wenn Sie das Verhalten ändern möchten, machen Sie die Methode öffentlich. Wenn Sie eine öffentliche Methode überschreiben, kann sie geändert werden.