Anonyme und innere Klassen

Anonyme und innere Klassen

Nach den bisher vorgestellten Prinzipien erfordert die Implementierung eines GUI die Implementierung von vielen Listenern und führt zur Erzeugung sehr vieler Klassen. Java erlaubt hier die Implementierung von anoymen, inneren Klassen die nur im Kontext einer bestimmten Klasse existieren und den Namensraum der Klassen nicht unnötig belasten.

Innere Klassen

Innere Klassen helfen es zu vermeiden, dass man Klassen veröffentlicht  die nur von genau einer anderen Klasse benutzt werden. Innere Klassen werden syntaktisch wie normale Klassen implementiert. Der einzige Unterschied ist, dass sie im Block einer äusseren Klasse implementiert werden. Sie werden in der äusseren Klasse mit dem gleichen Block der äusseren Klasse wie die Klassenvariablen und Methoden der äusseren Klasse implementiert.

Die in der Vorlesung vorgestellten inneren Klassen sind Elementklassen.

Elementklasse
Definition Elementklasse

Elementklassen sind wie Instanzmethoden und Instanzvariablen Elemente einer (anderen) Klasse. 

Sie werden auf der gleichen Blockebene wie Instanzmethoden und Instanzvariablen implementiert. Sie haben einen Zugriffschutz wie Instanzmethoden und Instanzvariablen.

Innere Klassen können auch als lokale Klasse innerhalb eines beliebigen Blocks implementiert werden. Diese Variante ist nicht Gegenstand der Vorlesung.

Die inneren Klassen gehören zum Paket der äusseren Klasse. Importkommandos müssen in der äusseren Klasse implementiert werden.

Besondere Eigenschaften von Elementklassen
  • Elementklassen und deren Instanzen können nur existieren, wenn ein Objekt der Sie umschließenden Klasse existiert.
  • Elementklassen haben den vollen Zugriff auf die Instanzvariablen und -methoden des umgebenden Objekts!

Instanzen von Elementklassen sind Komponenten des umgebenden Objekts!

Das vorhergehende Beispiel kann jetzt wie folgt implementiert werden:

package s2.swing;
 
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import javax.swing.JButton;
import javax.swing.JFrame;
 
/**
 *
 * @author s@scalingbits.com
 */
public class MouseAdapterInnereKlasseTest {
    /**
     * Die innere Klasses MyMouseListener
     */
    class MyMouseListener extends MouseAdapter {
        @Override
        public void mouseClicked(MouseEvent mEvent) {
            System.out.println("MouseClick wurde auf Position ["
                    + mEvent.getX() + ","
                    + mEvent.getY() + "] "
                    + mEvent.getClickCount() + " mal geklickt");
        }
    }
   /**
    * Erzeuge GUI im Konstruktor
    */
    public MouseAdapterInnereKlasseTest() {
        JFrame myJFrame = new JFrame("Mouse Click Innere Klasse Test");
        myJFrame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        JButton jb = new JButton("Hier drücken");
        jb.addMouseListener(new MyMouseListener());
        myJFrame.getContentPane().add(jb);
        myJFrame.pack();
        myJFrame.setVisible(true);
    }
    public static void main(String[] args) {
        MouseAdapterInnereKlasseTest mat =
                new MouseAdapterInnereKlasseTest();
    }
}

Die Unterschiede sind die Folgenden:

  • Die Klasse MouseAdapterInnereKlasseTest muss nicht mehr von der Klasse MouseAdapter abgeleitet werden oder eine Schnitstelle MouseEvent implementieren
  • Es wird eine eigene innere Klasse MyMouseListener implementiert die nicht ausserhalb der umgebenden Klasse bekannt ist.
  • Die Klasse MyMouseListener erbt (extends Schlüsselwort) von der Klasse MouseAdapter.
  • Beim Hinzufügen eines Listeners zum Button wird eine Instanz der inneren Klasse erzeugt.

Diese innere Klasse hat für den Entwickler den Vorteil, dass er keine neuen Klassen nach aussen hin bekanntmachen muss. Die Implementierung des Listeners erfolgt in der gleichen Datei. Die Implementierung des Listener ist also visuell näher als wenn sie in einer eigenen Klasse und einer eigenen Datei geschehen würde.

Objektabhängigkeit von Instanzen innerer Klassen

Im obigen Beispiel wird eine Instanz der Klasse MyMouseListener erzeugt. Dies ist nur erlaubt wenn das Objekt aus dem Kontext (Methode) eines Objekts der Klasse MouseAdapterInnereKlasseTest erzeugt wird. Dieses äussere Objekt existiert, da die innere Klasse im Konstruktor von MouseAdapterInnereKlasseTest erzeugt wird.

Der Code zum Erzeugen des GUI im Konstruktor kann nicht einfach in die statische main() Methode kopiert werden. Hier gibt es noch keinen Kontext zu einem Objekt der Klasse MouseAdapterInnereKlasseTest. Der javac Übersetzer wird einen Fehler melden.

Anonyme, innere Klasse

Für Swing wurde das Konzept der anonymen, inneren Klasse entwickelt.

Definition anonyme, innere Klasse

Eine anonyme, innere Klasse ist eine lokale Klasse ohne Namen die innerhalb eines Ausdrucks (Block) definiert und instanziiert wird.

Anonyme, innere Klassen haben keinen Namen und daher auch keine Konstruktoren. Sie werden typischerweise als Implementierungen für Adapterklassen oder Schnittstellen verwendet.

Beispiel

Anonyme, innere Klassen erlauben die benötigte Listenerimplementierung noch eleganter durchzuführen:

An der Stelle an der eine Instanz eines Listeners benötigt wird kann man auch direkt eine vollständige Klasse implementieren und instanziieren.

Das folgende Beispiel hat die gleiche Funktion wie das vorgehende Beispiel mit der Implementierung einer inneren Klasse. Es kommt aber ohne einen eigenen Klassennamen aus:

package s2.swing;
 
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import javax.swing.JButton;
import javax.swing.JFrame;

 
public class MouseAdapterAnonymeInnereKlasseTest {
   /**
    * Erzeuge GUI im Konstruktor
    */
    public MouseAdapterAnonymeInnereKlasseTest() {
        JFrame myJFrame = new JFrame("Mouse Click Adapter Test");
        myJFrame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        JButton jb = new JButton("Hier drücken");
        jb.addMouseListener(new MouseAdapter() {
            @Override
            public void mouseClicked(MouseEvent mEvent) {
                System.out.println("MouseClick wurde auf Position ["
                        + mEvent.getX() + ","
                        + mEvent.getY() + "] "
                        + mEvent.getClickCount() + " mal geklickt");
            }
        });
        myJFrame.getContentPane().add(jb);
        myJFrame.pack();
        myJFrame.setVisible(true);
    }
    public static void main(String[] args) {
        MouseAdapterAnonymeInnereKlasseTest mat =
                new MouseAdapterAnonymeInnereKlasseTest();
    }
}
Duke über Blueprint

Interessant

Eine anonyme, innere Klasse kann...

  • eine Schnittstelle implementieren
  • eine abstrakte Oberklasse spezialisieren
  • eine "normale" Klasse spezialisieren

Wichtig: Es müssen alle Methoden implementiert werden, die notwendig sind um die Klasse zu instanzieren

Die Unterschiede sind die Folgenden:

  • Die Klasse MouseAdapterInnereAnonymeKlasseTest muss nicht mehr von MouseAdapter abgeleitet werden oder einen MouseEvent implementieren
  • Beim Hinzufügen eines Listeners zum Button wird
    • mit dem new Operator eine Instanz einer anonymen Klasse angelegt, die die abstrakte Klasse MouseAdapter implementiert. Sie ist anonym da sie selbst keinen Namen besitzt.
    • die anonyme Klasse wird innerhalb der Klasse MouseAdapterInnereAnonymKlasseTest soweit wie nötig implementiert um aus der abstrakten Oberklasse eine normale Klasse zu erzeugen

Diese "Hilfskonstruktion" hat für den Entwickler eine Reihe von Vorteilen:

  • Das Ereignis kann textuell sehr nahe an der Erzeugung der Komponente implementiert werden. Der Code wird übersichtlicher
  • Es müssen keine neuen Klassen mit eigenen Namen erzeugt werden. Hierdurch wird die gesamte Klassenhierarchie übersichtlicher und deutlich kleiner.

Statische innere Klassen, nicht anonyme lokale Klassen

... sind nicht Gegenstand dieser Vorlesung. 

 

Stefan Schneider Sat, 03/05/2011 - 18:17

Anonymous (not verified)

Thu, 04/25/2013 - 21:10

Für das 1. Code Beispiel "MouseAdapterInnereKlasseTest", heißt die Innere Klasse nicht "MyMouseListener" anstatt wie in der Erklärung darunter "MyMouseClicked" beschrieben?

Entschuldigen Sie falls der Fehler schon in der Vorlesung gefunden wurde, ich war heute krank.
MfG

Danke für den Hinweis. Der Text wurde korrigiert. Die beiden Programmierbeispiele wurden um eine Methode entschlackt und sind jetzt kürzer und hoffentlich besser lesbar.

Müsste es dann in der Erklärung nicht statt:

"Die Klasse MyMouseClicked implementiert das MouseAdapter"

...

"Die Klasse MyMouseListener implementiert das MouseAdapter"

... heißen ?

Anonymous (not verified)

Wed, 05/15/2019 - 17:34

Hallo Herr Schneider,

mir ist ein kleiner Rechtschreibefehler aufgefallen. Bei den besonderen Eigenschaften von Elementklassen haben sie beim ersten Punkt umschliesen anstatt umschließen geschrieben.

freundliche Grüße