Innere Klassen

F: Wofür sind innere Klassen überhaupt gut?

A: Innere Klassen nisten in anderen Klassen. Eine normale Klasse ist ein direktes Mitglied eines Pakets, einer Klasse der obersten Ebene. Innere Klassen, die mit Java 1.1 verfügbar wurden, gibt es in vier Varianten:

  • Statische Mitgliedsklassen
  • Mitgliederklassen
  • Lokale Klassen
  • Anonyme Klassen

Lassen Sie uns nacheinander einen kurzen Blick darauf werfen.

Kurz gesagt, eine statische Elementklasse ist ein statisches Element einer Klasse. Wie jede andere statische Methode hat eine statische Elementklasse Zugriff auf alle statischen Methoden der übergeordneten oder übergeordneten Klasse.

Wie ein statisches Element Klasse, ein Mitglied der Klasse ist auch als Mitglied einer Klasse definiert. Im Gegensatz zur statischen Variante ist die Member-Klasse instanzspezifisch und hat Zugriff auf alle Methoden und Member, auch auf die thisReferenz des übergeordneten Elements.

Lokale Klassen werden innerhalb eines Codeblocks deklariert und sind wie jede andere Methodenvariable nur innerhalb dieses Blocks sichtbar.

Schließlich ist eine anonyme Klasse eine lokale Klasse, die keinen Namen hat.

Um Ihre spezifische Frage zu beantworten, werde ich mich auf die Mitglieder- und anonymen inneren Klassen konzentrieren, da dies diejenigen sind, denen Sie wahrscheinlich begegnen und die Sie verwenden werden. Für mich lassen sich die Vorteile innerer Klassen in drei Kategorien einteilen: einen objektorientierten Vorteil, einen organisatorischen Vorteil und einen Rückrufvorteil.

Der objektorientierte Vorteil

Meiner bescheidenen Meinung nach ist das wichtigste Merkmal der inneren Klasse, dass Sie damit Dinge in Objekte verwandeln können, die Sie normalerweise nicht in Objekte verwandeln würden. Dadurch kann Ihr Code noch objektorientierter sein als ohne innere Klassen.

Schauen wir uns die Mitgliederklasse an. Da seine Instanz Mitglied der übergeordneten Instanz ist, hat sie Zugriff auf jedes Mitglied und jede Methode im übergeordneten Element. Auf den ersten Blick scheint dies nicht viel zu sein; Wir haben diese Art von Zugriff bereits innerhalb einer Methode in der übergeordneten Klasse. Mit der Member-Klasse können wir jedoch die Logik aus dem übergeordneten Element entfernen und objektivieren. Beispielsweise kann eine Baumklasse eine Methode und viele Hilfsmethoden haben, die eine Suche oder einen Spaziergang durch den Baum durchführen. Aus objektorientierter Sicht ist der Baum ein Baum, kein Suchalgorithmus. Sie benötigen jedoch genaue Kenntnisse der Datenstrukturen des Baums, um eine Suche durchzuführen.

Eine innere Klasse ermöglicht es uns, diese Logik zu entfernen und in ihre eigene Klasse zu ordnen. Aus objektorientierter Sicht haben wir die Funktionalität dort entfernt, wo sie nicht hingehört, und sie in eine eigene Klasse eingeordnet. Durch die Verwendung einer inneren Klasse haben wir den Suchalgorithmus erfolgreich vom Baum entkoppelt. Um den Suchalgorithmus zu ändern, können wir einfach eine neue Klasse austauschen. Ich könnte weitermachen, aber das öffnet unseren Code für viele der Vorteile, die objektorientierte Techniken bieten.

Der organisatorische Vorteil

Objektorientiertes Design ist nicht jedermanns Sache, aber zum Glück bieten innere Klassen mehr. Aus organisatorischer Sicht ermöglichen uns innere Klassen, unsere Paketstruktur mithilfe von Namespaces weiter zu organisieren. Anstatt alles in einem flachen Paket abzulegen, können Klassen weiter in Klassen verschachtelt werden. Ohne innere Klassen waren wir explizit auf die folgende Hierarchiestruktur beschränkt:

Paket1 Klasse 1 Klasse 2 ... Klasse n ... Paket n 

Mit inneren Klassen können wir Folgendes tun:

Paket 1 Klasse 1 Klasse 2 Klasse 1 Klasse 2 ... Klasse n 

Bei sorgfältiger Verwendung können innere Klassen eine strukturelle Hierarchie bereitstellen, die natürlicher zu Ihren Klassen passt.

Der Rückrufvorteil

Sowohl innere Mitgliedsklassen als auch anonyme Klassen bieten eine bequeme Methode zum Definieren von Rückrufen. Das offensichtlichste Beispiel betrifft den GUI-Code. Die Anwendung des Rückrufs kann sich jedoch auf viele Domänen erstrecken.

Die meisten Java-GUIs verfügen über eine Komponente, die einen actionPerformed()Methodenaufruf auslöst. Leider lassen die meisten Entwickler einfach ihr Hauptfenster implementieren ActionListener. Infolgedessen verwenden alle Komponenten dieselbe actionPerformed()Methode. Um herauszufinden, welche Komponente die Aktion ausgeführt hat, gibt es normalerweise einen riesigen, hässlichen Schalter in der actionPerformed()Methode.

Hier ist ein Beispiel für eine monolithische Implementierung:

öffentliche Klasse SomeGUI erweitert JFrame implementiert ActionListener {protected JButton button1; geschützte JButton-Taste2; ... geschützter JButton buttonN; public void actionPerformed (ActionEvent e) {if (e.getSource () == button1) {// etwas tun} else if (e.getSource () == button2) {... Sie erhalten das Bild

Jedes Mal , wenn Sie Schalter oder große sehen if/ if elseBlöcke, lauter Alarmglocken sollten klingeln im Kopf beginnen. Im Allgemeinen sind solche Konstrukte ein schlechtes objektorientiertes Design, da eine Änderung in einem Abschnitt des Codes eine entsprechende Änderung in der switch-Anweisung erfordern kann. Innere Mitgliedsklassen und anonyme Klassen ermöglichen es uns, von der geschalteten actionPerformed()Methode wegzukommen .

Stattdessen können wir eine innere Klasse definieren, die ActionListenerfür jede Komponente implementiert wird , die wir abhören möchten. Das kann zu vielen inneren Klassen führen. Wir können jedoch große switch-Anweisungen vermeiden und haben den zusätzlichen Vorteil, unsere Aktionslogik zu kapseln. Darüber hinaus kann dieser Ansatz die Leistung verbessern. Bei einem Wechsel mit n Vergleichen können wir im Durchschnitt n / 2 Vergleiche erwarten . Innere Klassen ermöglichen es uns, eine 1: 1-Korrespondenz zwischen dem Action-Performer und dem Action-Listener einzurichten. In einer großen Benutzeroberfläche können solche Optimierungen einen erheblichen Einfluss auf die Leistung haben. Ein anonymer Ansatz könnte folgendermaßen aussehen:

öffentliche Klasse SomeGUI erweitert JFrame {... Schaltflächenelementdeklarationen ... protected void buildGUI () {button1 = new JButton (); button2 = neuer JButton (); ... button1.addActionListener (new java.awt.event.ActionListener () {public void actionPerformed (java.awt.event.ActionEvent e) {// etwas tun}}); .. für jede Taste wiederholen

Bei Verwendung innerer Elementklassen würde dasselbe Programm folgendermaßen aussehen:

public class SomeGUI erweitert JFrame {... Schaltflächenelementdeklarationen // innere Klassendefinitionsklasse Button1Handler implementiert ActionListener {public void actionPerformed (ActionEvent e) {// etwas tun}} ... definiert eine innere Elementklasse für jede schaltflächengeschützte void buildGUI () {// initialisiere die Buttons button1 = new JButton (); button2 = neuer JButton (); ... // eine innere Klassenaktions-Listener-Instanz registrieren // für jede Schaltfläche button1.addActionListener (new Button1Handler ()); .. für jede Taste wiederholen

Da innere Klassen Zugriff auf alles im übergeordneten Element haben, können wir jede Logik, die in einer monolithischen actionPerformed()Implementierung aufgetreten wäre, in eine innere Klasse verschieben.

Ich bevorzuge es, Mitgliedsklassen als Rückrufe zu verwenden. Dies ist jedoch eine Frage der persönlichen Präferenz. Ich habe einfach das Gefühl, dass zu viele anonyme Klassen Code durcheinander bringen. Ich habe auch das Gefühl, dass anonyme Klassen unhandlich werden können, wenn sie größer als eine oder zwei Zeilen sind.

Nachteile?

Wie bei allem anderen muss man das Gute mit dem Schlechten nehmen. Innere Klassen haben ihre Nachteile. Unter Wartungsgesichtspunkten ist es für unerfahrene Java-Entwickler möglicherweise schwierig, die innere Klasse zu verstehen. Die Verwendung innerer Klassen erhöht auch die Gesamtzahl der Klassen in Ihrem Code. Darüber hinaus haben die meisten Java-Tools aus entwicklungspolitischer Sicht einen Mangel an Unterstützung für innere Klassen. Zum Beispiel verwende ich IBMs VisualAge für Java für meine tägliche Codierung. Während innere Klassen in VisualAge kompiliert werden, gibt es keinen Browser oder eine Vorlage für innere Klassen. Stattdessen müssen Sie einfach die innere Klasse direkt in die Klassendefinition eingeben. Das macht das Durchsuchen der inneren Klasse leider schwierig. Es ist auch schwierig zu tippen, da Sie viele VisualAge verlieren.Die Code-Vervollständigung hilft, wenn Sie in die Klassendefinition eingeben oder eine innere Klasse verwenden.

Tony Sintes ist Senior Consultant bei ObjectWave und auf Telekommunikation spezialisiert. Sintes, ein Sun-zertifizierter Java 1.1-Programmierer und Java 2-Entwickler, arbeitet seit 1997 mit Java.

Erfahren Sie mehr über dieses Thema

  • "Inner Classes Specification" von Sun bietet einen detaillierten Einblick in innere Klassen

    //java.sun.com/products/jdk/1.1/docs/guide/innerclasses/spec/innerclasses.doc.html

Diese Geschichte "Innere Klassen" wurde ursprünglich von JavaWorld veröffentlicht.