Lambda Ausdrücke

Login erforderlich

Du musst Dich einloggen, um alle Inhalte zu sehen.

GUI mit Lambda-Ausdrücken

Sehr praktisch sind Lambda-Ausdrücke u.a. bei der GUI-Programmierung. Eine Beispielprogression, wie man von einem JFrame, das ActionListener implementiert, über (anonyme) innere Klassen zu Lambda-Ausdrücken kommt, findest Du hier.

Wir gehen von einem JFrame aus, das zwei Buttons enthält, und das Interface ActionListener implementiert.

import java.awt.*;
import java.awt.event.*;
import javax.swing.*;

public class MeineGuiMitActionListener extends JFrame implements ActionListener{
	JButton klickMichButton;
	JButton exitButton;
	
	public MeineGuiMitActionListener() {
		klickMichButton = new JButton("Klick mich");
		klickMichButton.addActionListener(this);
		add(klickMichButton);
		
		exitButton = new JButton("Beenden");
		exitButton.addActionListener(this);
		add(exitButton);
		
		setLayout(new FlowLayout());
		pack();
		setDefaultCloseOperation(EXIT_ON_CLOSE);
		setVisible(true);
	}
	
	public void actionPerformed(ActionEvent e) {
		if(e.getSource() == klickMichButton)
			klickAktionAusfuehren();
		if(e.getSource() == exitButton)
			beenden();
	}

	private void klickAktionAusfuehren() {
		JOptionPane.showMessageDialog(this, "Yippie, der Button wurde geklickt");
	}

	private void beenden() {
		System.exit(0);
	}
}

Um den Code zu vereinfachen, gehen wir zuerst den Umweg über weitere Klassen. Wir definieren dazu innere Klassen, die nur dazu da sind, um die Ereignisbehandlung für genau ein Ereignis zu behandeln.

// import-Anweisungen ausgelassen

public class MeineGuiInnereKlassen extends JFrame{ // implementiert nicht mehr ActionListener
	JButton klickMichButton;
	JButton exitButton;
	
	class KlickMichButtonHandler implements ActionListener {
		public void actionPerformed(ActionEvent e) {
			klickAktionAusfuehren();
		}
	}
	
	class ExitButtonHandler implements ActionListener {
		public void actionPerformed(ActionEvent e) {
			beenden();
		}		
	}
	
	public MeineGuiInnereKlassen() {
		klickMichButton = new JButton("Klick mich");
		klickMichButton.addActionListener(new KlickMichButtonHandler());
		add(klickMichButton);
		
		exitButton = new JButton("Beenden");
		exitButton.addActionListener(new ExitButtonHandler());
		add(exitButton);
		
		// restliche Initialisierung ausgelassen
	}

	private void klickAktionAusfuehren() {
		JOptionPane.showMessageDialog(this, "Yippie, der Button wurde geklickt");
	}

	private void beenden() {
		System.exit(0);
	}
}

Da man die Objekte zur Ereignisbehandlung genau einmal erzeugt, macht es Sinn anonyme innere Klassen zu benutzen:

public class MeineGuiAnonym extends JFrame{
	JButton klickMichButton;
	JButton exitButton;
	
	public MeineGuiAnonym() {
		klickMichButton = new JButton("Klick mich");
		klickMichButton.addActionListener(new ActionListener() {
			public void actionPerformed(ActionEvent e) {
				klickAktionAusfuehren();
			}
		} );
		add(klickMichButton);
		
		exitButton = new JButton("Beenden");
		exitButton.addActionListener(new ActionListener() {
			public void actionPerformed(ActionEvent e) {
				beenden();
			}
		} );
		add(exitButton);
		
		// weitere Initialisierungen
	}

	private void klickAktionAusfuehren() {
		JOptionPane.showMessageDialog(this, "Yippie, der Button wurde geklickt");
	}

	private void beenden() {
		System.exit(0);
	}
}

Das Interface ActionListener ist ein "functional Interface", d.h. es besitzt nur eine einzige Methode, definiert also quasi eine Funktion. In solch einem Fall kann man statt der anonymen inneren Klasse einen Lambda-Ausdruck verwenden. Diesen kann man in unserem Fall zusätzlich verkürzen:

public class MeineGuiLambda extends JFrame{
	JButton klickMichButton;
	JButton exitButton;
	
	public MeineGuiLambda() {
		klickMichButton = new JButton("Klick mich");
		klickMichButton.addActionListener((ActionEvent e) -> {
			klickAktionAusfuehren();
		});
		add(klickMichButton);
		
		// maximal reduziert:
		exitButton = new JButton("Beenden");
		exitButton.addActionListener(e -> beenden());
		add(exitButton);
		
		// weitere Initialisierungen
	}

	private void klickAktionAusfuehren() {
		JOptionPane.showMessageDialog(this, "Yippie, der Button wurde geklickt");
	}

	private void beenden() {
		System.exit(0);
	}
}

Übung - Tippspiel

Gegeben ist die folgende Implementierung eines sehr einfachen Spiels, bei dem man so lange eine Zahl raten muss, bis man die richtige Zahl getippt hat. Es soll möglich sein die Zufallszahlen auf unterschiedliche Art zu erzeugen. Denkbar wären z.B. unterschiedliche Intervalle, Gauß-verteilte Zahlen etc. Im Beispiel sollen Zahlen von 0 bis 9 möglich sein. Zum Testen des Spiels soll auch eine Variante mit einer festen Zahl gespielt werden können.

Baue die Klasse Tippspiel so um, dass

  1. anonyme innere Klassen
  2. Lambda-Ausdrücke in vollständiger Form
  3. Lambda-Ausdrücke möglichst knapp

benutzt werden.

public interface Zufallszahlengenerator {
    int getZahl();
}


public class Tippspiel {

	Zufallszahlengenerator zg;

	Tippspiel() {
		zg = new SimpleZahlGenerator();
	}

	void eineRundeSpielen() {
		int zufallszahl = zg.getZahl();
		int tipp = new java.util.Scanner(System.in).nextInt();
		while (tipp != zufallszahl) {
			tipp = new java.util.Scanner(System.in).nextInt();
		}
		System.out.println("Richtig geraten!");
	}

	void testVorbereiten() {
		// Denkbar wäre hier auch völlig andere Verteilungen zu erstellen.
		zg = new KonstantZahlGenerator();
	}

	class SimpleZahlGenerator implements Zufallszahlengenerator {

		public int getZahl() {
			return new java.util.Random().nextInt(10);
		}
	}

	class KonstantZahlGenerator implements Zufallszahlengenerator {

		public int getZahl() {
			return 42;
		}
	}

	public static void main(String[] args) {
		Tippspiel zahlenspiel = new Tippspiel();
		System.out.println("Jetzt eine richtige Runde spielen");
		zahlenspiel.eineRundeSpielen();

		System.out.println("Eine Runde mit der Testzahl spielen");
		zahlenspiel.testVorbereiten();
		zahlenspiel.eineRundeSpielen();
	}
}