Wie reagiert ein Button beim Anklicken?

Schaltflächen wie JButton erzeugen bei Benutzung, wie viele andere Komponenten auch, sog. events (Ereignisse). Je nach Komponente werden unterschiedliche Ereignisse ausgesandt. Bei Schaltflächen ist dies ein ActionEvent.

Da man normalerweise nicht an allen ausgesandten Ereignissen interessiert ist, muss man diejenigen abfangen, auf die reagiert werden soll. Hierzu steht für jede spezielle Ereignisart ein sog. Listener zur Verfügung, bei dem man die das Ereignis erzeugende Komponente anmelden muss. Der ActionListener selbst ist eine Schnittstelle mit einer Methode, void actionPerformed(ActionEvent e), die von der Klasse implementiert werden muss, die als Listener fungieren soll.
In den folgenden Beispielen werden hierzu die gängigsten Verfahren anhand einer einfachen Schaltfläche vorgestellt, die bei Betätigung einen kurzen Text auf der Konsole ausgibt.

Listener und Button in der selben Klasse

import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.util.Random;

import javax.swing.JButton;
import javax.swing.JFrame;

public class ButtonEventClass extends JFrame implements ActionListener {
    
    private JButton button;
    
    public ButtonEventClass(){
        button = new JButton("click mich!");
        button.addActionListener(this);
        
        this.getContentPane().add(button);
    }
    
    public void actionPerformed(ActionEvent e) {
        if(e.getSource() == button){
            System.out.println("Button geklickt!");
        }
    }
    
    public static void main(String[] args){
        ButtonEventClass bec = new ButtonEventClass();
        bec.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        bec.setSize(200, 200);
        bec.setVisible(true);
    }
}

Im ersten Beispiel implementiert die Klasse, die den Button enthält, den Listener selbst. Somit muss sie selbst auch die Methode actionPerformed() enthalten, die als Parameter ein ActionEvent übergeben bekommt.
Der Ereignisauslöser muss jetzt durch die Methode addActionListener()mit dem Listener verbunden werden. Als Parameter wird ein Verweis auf das aktuelle Objekt der eigenen Klasse übergeben, da die Klasse ja selbst den Listener implementiert.

Listener in innerer Klasse

import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.util.Random;

import javax.swing.JButton;
import javax.swing.JFrame;

public class ButtonEvent2Class extends JFrame {
    
    private JButton button;
    
    public ButtonEvent2Class(){
        button = new JButton("click mich!");
        button.addActionListener(new ButtonLauscher());
        
        this.getContentPane().add(button);
    }
    
    class ButtonLauscher implements ActionListener {
        public void actionPerformed(ActionEvent e) {
            if(e.getSource() == button){
                System.out.println("Button geklickt!");
            }
        }
    }
    
    public static void main(String[] args){
        ButtonEvent2Class bec = new ButtonEvent2Class();
        bec.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        bec.setSize(200, 200);
        bec.setVisible(true);
    }
}

Im zweiten Beispiel wird der ActionListener in einer eigenen inneren Klasse realisiert, von der eine Instanz mit dem Button verbunden wird. Wie im ersten Beispiel auch kann mit der Methode ActionEvent.getSource() die Ereignisquelle ermittelt werden. Auf diese Weise kann ein Listener mehrere Ereignisse überwachen.

Listener in separater Klasse

import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.util.Random;

import javax.swing.JButton;
import javax.swing.JFrame;

public class ButtonEvent3Class extends JFrame {
    
    private JButton button;
    
    public ButtonEvent3Class(){
        button = new JButton("click mich!");
        button.addActionListener(new ButtonLauscher());
        
        this.getContentPane().add(button);
    }
    
    
    
    public static void main(String[] args){
        ButtonEvent3Class bec = new ButtonEvent3Class();
        bec.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        bec.setSize(200, 200);
        bec.setVisible(true);
    }
}

class ButtonLauscher implements ActionListener {
    public void actionPerformed(ActionEvent e) {
            System.out.println("Button '" + ((JButton)e.getSource()).getText() + "' geklickt.");
    }
}

Das dritte Beispiel entspricht in den Grundzügen dem zweiten. Jedoch ist der Listener nicht als innere, sondern als selbstständige Klasse realisiert. Da hier die Instanz des auslösenden JButton nicht mehr zur Verfügung steht, ist ein Abgleich mit

if(e.getSource() == button){ //...

wie in Beispiel 2 so nicht mehr möglich. Die Lösung zeigt die Version 4. In dieser wird der Listener-Klasse das Objekt des auslösenden Buttons als Parameter übergeben und im Konstruktor an eine Instanzvariable weitergeleitet. Diese kann dann auf die bekannte Weise abgefragt werden.

import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.util.Random;

import javax.swing.JButton;
import javax.swing.JFrame;

public class ButtonEvent4Class extends JFrame {
    
    private JButton button;
    
    public ButtonEvent4Class(){
        button = new JButton("click mich!");
        button.addActionListener(new ButtonLauscher4(button));
        
        this.getContentPane().add(button);
    }
    
    
    
    public static void main(String[] args){
        ButtonEvent4Class bec = new ButtonEvent4Class();
        bec.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        bec.setSize(200, 200);
        bec.setVisible(true);
    }
}

class ButtonLauscher4 implements ActionListener {
    JButton button;
    public ButtonLauscher4(JButton button){
        this.button = button;
    }
    public void actionPerformed(ActionEvent e) {
        if(e.getSource() == button)
            System.out.println("Button '" + ((JButton)e.getSource()).getText() + "' geklickt.");
    }
}

Listener in anonymer Klasse

Die für einfache Fälle naheliegendste Möglichkeit besteht jedoch darin, den Listener in einer anonymen Klasse zu implementieren.

public class ButtonEventAnonClass extends JFrame {

    private JButton button;

    public ButtonEventAnonClass() {
        button = new JButton("click mich!");
        button.addActionListener(new ActionListener() {
            public void actionPerformed(ActionEvent e) {
                System.out.println("Button geklickt!");
            }
        });
        this.getContentPane().add(button);
    }

    public static void main(String[] args) {
        ButtonEventAnonClass bec = new ButtonEventAnonClass();
        bec.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        bec.setSize(200, 200);
        bec.setVisible(true);
    }
}

Listener als Lambda Ausdruck

Ab Java 8 kann statt der anonymen Klasse auch ein Lambda-Ausdruck verwendet werden, da es sich beim ActionListener um ein functional interface handelt. Der Listener-Aufruf kann dann in der folgenden Weise formuliert werden.

//...
button = new JButton("click mich!");
button.addActionListener(e -> System.out.println("Button geklickt"));
//...