Bei dieser Frage geht es darum, ob eine verschachtelte Klasse in Java eine statische verschachtelte Klasse oder eine innere verschachtelte Klasse sein soll. Ich habe hier und auf Stack Overflow gesucht, konnte aber keine wirklichen Fragen zu den Auswirkungen dieser Entscheidung auf das Design finden.
Die Fragen, die ich gefunden habe, beziehen sich auf den Unterschied zwischen statischen und inneren verschachtelten Klassen, was mir klar ist. Ich habe jedoch noch keinen überzeugenden Grund gefunden, jemals eine statische verschachtelte Klasse in Java zu verwenden - mit Ausnahme von anonymen Klassen, die ich für diese Frage nicht berücksichtige.
Hier ist mein Verständnis der Auswirkungen der Verwendung statisch verschachtelter Klassen:
- Weniger Kopplung: Im Allgemeinen erhalten wir weniger Kopplung, da die Klasse nicht direkt auf die Attribute ihrer äußeren Klasse zugreifen kann. Weniger Kopplung bedeutet im Allgemeinen eine bessere Codequalität, einfacheres Testen, Refactoring usw.
- Single
Class
: Der Klassenlader muss sich nicht jedes Mal um eine neue Klasse kümmern, sondern erstellt ein Objekt der äußeren Klasse. Wir bekommen immer wieder neue Objekte für die gleiche Klasse.
Für eine innere Klasse empfinde ich den Zugang zu den Attributen der äußeren Klasse im Allgemeinen als einen Vorteil. Ich möchte diesbezüglich einen konstruktiven Unterschied machen, da dieser direkte Zugriff eine hohe Kopplung bedeutet. Wenn wir die verschachtelte Klasse jemals in eine separate Klasse der obersten Ebene extrahieren möchten, können wir dies erst tun, nachdem wir uns im Wesentlichen umgedreht haben es in eine statische verschachtelte Klasse.
Meine Frage lautet also: Liege ich falsch in der Annahme, dass der für nicht statische innere Klassen verfügbare Attributzugriff zu einer hohen Kopplung und damit zu einer geringeren Codequalität führt und dass ich daraus schließen sollte, dass (nicht anonyme) verschachtelte Klassen vorhanden sind in der Regel statisch sein?
Oder mit anderen Worten: Gibt es einen überzeugenden Grund, warum man eine verschachtelte innere Klasse bevorzugen würde?
Antworten:
Joshua Bloch in Punkt 22 seines Buches "Effective Java Second Edition" erklärt, wann welche Art von verschachtelter Klasse zu verwenden ist und warum. Es gibt einige Anführungszeichen unten:
Eine häufig verwendete Verwendung einer statischen Memberklasse ist eine öffentliche Hilfsklasse, die nur in Verbindung mit ihrer äußeren Klasse nützlich ist. Betrachten Sie beispielsweise eine Aufzählung, die die von einem Taschenrechner unterstützten Vorgänge beschreibt. Die Operation enum sollte eine öffentliche statische Memberklasse der
Calculator
Klasse sein. Clients vonCalculator
könnten dann mit Namen wieCalculator.Operation.PLUS
und auf Operationen verweisenCalculator.Operation.MINUS
.Eine gebräuchliche Verwendung einer nicht statischen Memberklasse besteht darin, einen Adapter zu definieren , mit dem eine Instanz der äußeren Klasse als eine Instanz einer nicht verwandten Klasse betrachtet werden kann. Beispielsweise verwenden Implementierungen der
Map
Schnittstelle in der Regel nicht statische Memberklassen, um ihre Auflistungsansichten , die vonMap
s zurückgegebenkeySet
werdenentrySet
, undvalues
Methoden zu implementieren . In ähnlicher Weise verwenden Implementierungen der Auflistungsschnittstellen, z. B.Set
undList
, normalerweise nicht-statische Member-Klassen, um ihre Iteratoren zu implementieren:Wenn Sie ein Mitglied der Klasse deklarieren , die keinen Zugang zu einer umschließenden Instanz erfordern, immer setzt den
static
Modifikator in seiner Erklärung, es ist eine statisches machte eher als eine nicht statische Elementklasse.quelle
MySet
? Ich könnte mir vorstellen, dass so etwas wie ein pfadabhängiger Typ ein Unterschied istmyOneSet.iterator.getClass() != myOtherSet.iterator.getClass()
, aber in Java ist dies eigentlich nicht möglich, da die Klasse für beide gleich wäre.Sie gehen zu Recht davon aus, dass der für nicht statische innere Klassen verfügbare Attributzugriff zu einer hohen Kopplung und damit zu einer geringeren Codequalität führt und (nicht anonyme und nicht lokale ) innere Klassen im Allgemeinen statisch sein sollten.
Die Auswirkungen der Entscheidung, die innere Klasse nicht statisch zu machen, auf das Design sind in Java Puzzlers , Puzzle 90 (die fett gedruckte Schrift im folgenden Zitat stammt von mir) aufgeführt:
Wenn Sie interessiert sind, finden Sie in dieser Antwort unter Stapelüberlauf eine detailliertere Analyse von Puzzle 90 .
Es ist erwähnenswert, dass es sich bei der obigen Beschreibung im Wesentlichen um eine erweiterte Version der Anleitung handelt, die im Lernprogramm für Java- Klassen und -Objekte enthalten ist :
Die Antwort auf die Frage, die Sie in anderen Worten pro Lernprogramm gestellt haben, ist der einzige überzeugende Grund für die Verwendung von nicht statisch, wenn der Zugriff auf die nicht öffentlichen Felder und Methoden einer einschließenden Instanz erforderlich ist .
Der Wortlaut des Tutorials ist ziemlich weit gefasst (dies könnte der Grund sein, warum Java-Puzzler versuchen, es zu stärken und einzugrenzen). Insbesondere der direkte Zugriff auf umschließende Instanzfelder war nach meiner Erfahrung nie wirklich erforderlich - in dem Sinne, dass alternative Methoden wie die Übergabe als Konstruktor- / Methodenparameter immer einfacher zu debuggen und zu warten waren.
Insgesamt machte meine (ziemlich schmerzhafte) Begegnung mit dem Debuggen innerer Klassen, die direkt auf Felder umschließender Instanzen zugreifen, den starken Eindruck, dass diese Praxis der Verwendung des globalen Staates und der damit verbundenen bekannten Übel ähnelt .
Natürlich macht Java es so, dass der Schaden einer solchen "quasi globalen" Klasse in der einschließenden Klasse enthalten ist, aber als ich eine bestimmte innere Klasse ausprüfen musste, fühlte es sich so an, als würde eine solche Pflasterung nicht dazu beitragen, die Schmerzen zu lindern "fremde" Semantik und Details im Auge zu behalten, anstatt sich ganz auf die Analyse eines bestimmten störenden Objekts zu konzentrieren.
Der Vollständigkeit halber kann es Fälle geben, in denen die obigen Überlegungen nicht zutreffen. Beispielsweise deutet diese Funktion beim Lesen von " map.keySet" -Javadocs auf eine enge Kopplung hin und macht daher Argumente gegen nicht statische Klassen ungültig:
Nicht das obige würde es irgendwie einfacher machen, den beteiligten Code zu warten, zu testen und zu debuggen, aber es könnte einem zumindest erlauben zu argumentieren, dass die Komplikation mit der beabsichtigten Funktionalität übereinstimmt / gerechtfertigt ist.
quelle
Einige Missverständnisse:
IIRC - Eigentlich kann es. (Wenn es sich natürlich um Objektfelder handelt, muss kein äußeres Objekt betrachtet werden. Wenn es jedoch ein solches Objekt von einer beliebigen Stelle erhält, kann es direkt auf diese Felder zugreifen.)
Ich bin mir nicht ganz sicher, was du hier meinst. Der Klassenlader ist insgesamt nur zweimal beteiligt, einmal für jede Klasse (wenn die Klasse geladen ist).
Wandern folgt:
In Javaland scheinen wir ein bisschen Angst zu haben, Klassen zu schaffen. Ich denke, es muss sich wie ein wichtiges Konzept anfühlen. Es gehört in der Regel in eine eigene Datei. Es braucht eine signifikante Kesselplatte. Es muss auf sein Design geachtet werden.
Gegen andere Sprachen - zB Scala oder vielleicht C ++: Es gibt absolut kein Drama, das einen winzigen Halter (Klasse, Struktur, was auch immer) schafft, wenn zwei Datenbits zusammengehören. Flexible Zugriffsregeln helfen Ihnen dabei, die Heizplatte zu verkleinern und den zugehörigen Code physisch näher zu halten. Dies verringert Ihre "Gedankendistanz" zwischen den beiden Klassen.
Wir können Design-Bedenken in Bezug auf den direkten Zugang abwenden, indem wir die innere Klasse privat halten.
(... Wenn Sie gefragt hätten, warum eine nicht-statische öffentliche innere Klasse?)
Sie können sie jederzeit verwenden, um die Lesbarkeit des Codes zu verbessern und DRY-Verstöße und Boilerplate zu vermeiden. Ich habe kein fertiges Beispiel, weil es meiner Erfahrung nach nicht allzu oft vorkommt, aber es passiert.
Statische innere Klassen sind übrigens immer noch ein guter Standardansatz . Nicht statisch wird ein zusätzliches verstecktes Feld generiert, durch das sich die innere Klasse auf die äußere bezieht.
quelle
Es kann verwendet werden, um ein Factory-Muster zu erstellen. Ein Factory-Muster wird häufig verwendet, wenn die erstellten Objekte zusätzliche Konstruktorparameter benötigen, um zu funktionieren.
Erwägen:
Benutzt als:
Dasselbe könnte mit einer nicht statischen inneren Klasse erreicht werden:
Benutzen als:
Fabriken profitieren von speziell genannten Verfahren mit, wie
create
,createFromSQL
odercreateFromTemplate
. Dasselbe kann auch hier in Form mehrerer innerer Klassen geschehen:Es ist weniger Parameterübergabe von der Fabrik zur konstruierten Klasse erforderlich, aber die Lesbarkeit kann ein wenig leiden.
Vergleichen Sie:
vs
quelle