# ActionListener und GUI



## knowledge (12. Mrz 2009)

Hallo,

um ein ausgelöstes Event zu verarbeiten bedarf es ja eines Listeners. Prinzipiell ist es ja so, dass das Event auslösende Objekt nur Wissen muss, dass die Ereignisempfänger das bestimmte Listener Interface implementieren und dort eine bestimmte Methode (z.B. actionPerformed) vorhanden ist, die aufgerufen werden kann. Dort führe ich dann meine Aktionen durch, die bei dem Event ausgeführt werden sollen, z.B. andere Methoden aufrufen. 

Nur prinzipiell... Was wäre so schlimm gewesen, wenn man statt add(Listener) sowas wie add(Listener(MeineMethode) einführen würde, d.h. ich könnte bestimmen welche Methode (statt actionPerformed) aufgerufen werden könnte.

Angenommen ein Buttonklick soll die Methode "rechne()" aufrufen. Dann würde ich ja bisher add(Listener) durchführen und der Listener implementiert actionPerformed und darin rufe ich dann rechne() auf. Wäre es nicht schöner, wenn man direkt eigene Methodennamen angeben könnte, das also nicht actionPerfomed sondern gleich rechne() aufgerufen wird? 

Nachteil an der Sache wäre doch nur, dass nun statt ein stures aufrufen von actionPerfomed nun halt für jeden registrierten Listener ggf. eine andere, eigene Methode aufgerufen werden müsste, d.h. es muss nun etwas mehr gewusst werden als das nur das Listener Interface implementiert wurde. 

Wären mit dieser Lösung, dass man eigene Methoden aufrufen lassen könnte, statt nur z.B. actionPerformed, gravierende Nachteile verbunden und wenn ja welche. D.h. was ist der Vorteil von der jetzigen Lösung, was wäre der Nachteil von dieser "meiner" Lösung???


----------



## The_S (12. Mrz 2009)

Der Nachteil an deiner Methode ist die Realisierbarkeit. Wie willst du das (außer mit unsicheren Reflection-Gewurstel und dann doch einem Listener, der zwischengeschaltet wird) anstellen?


----------



## SlaterB (12. Mrz 2009)

wie du mehr oder weniger erkannt hast, kannst du eine actionPerformed() schreiben, die dann rechne() aufruft,
das ist der ganz normale logische Fluss, das würde niemand ändern wollen, denn innerhalb von JButton oder anderen 'Event auslösenden Objekten' steht explizit drin
for (alle ActionListener) {
actionPerformed();
} 


nun gehts allein noch darum ob man
button.addActionListener( lange Definition eines Listeners der rechne() aufruft);
schreibt oder 
button.addActionListener( rechne() );
oder ähnlich kurz mit intern dem gleichen Ergebnis

das ist inhaltlich kein Problem, sondern nur eine Frage der Syntax, Lesbarkeit, Kompilierbarkeit usw.,
in Java gibts das nicht, in anderen Sprachen schon, Stichwort Closures, wenn ich das richtig erinnere

Java Blog 'Java ist auch eine Insel: Java Closures : Inselupdate für Java 7' | Deutsches Java Weblog


----------



## Marco13 (12. Mrz 2009)

Irgendwann hatte ich (nur testweise!!!) einen "GenericListener" gebastelt, der mit Reflection solche Möglichkeiten bietet. Aber dass es allgemein "sprachintern" nicht angeboten wird, hat die oben genannten Gründe.


----------



## knowledge (12. Mrz 2009)

>Der Nachteil an deiner Methode ist die Realisierbarkeit. Wie willst du das (außer mit unsicheren Reflection-Gewurstel >und dann doch einem Listener, der zwischengeschaltet wird) anstellen?

Hmm, was wäre so schwierig daran zu sagen es handelt sich hier eigentlich um Klasse xy auf der die Methode rechne aufgerufen werden soll. In der Schleife müsste dann statt 

for(alle ActionListener l) { l.actionPerformed() } sowas wie

for(Object o, Klasse, Methode rechne) { Klasse k = (Klasse) o; k.rechne(); } So stell ich mir das vor. Also gesetz dem Fall das wäre realisierbar ist das doch nur ein geringer Overhead. Müssen halt ein paar zusätzliche Angaben (welche Klasse, welche Methode) übergeben werden.


----------



## Marco13 (12. Mrz 2009)

Ja, das wäre ein Spezialfall. Es wäre ja ... absurd, wenn man in so einem mächtigen Universalwerkzeug einen sprachinternen Mechanismus einführen würde, mit dem man "In GUI-Anwendungen an Buttons eine Methode hängen kann, damit man sich die Zeile mit dem ActionListener spart". 

WENN so etwas eingeführt wird, dann auf VIEL (VIEL VIEL VIEL) allgemeinerer, abstrakterer Basis - und diese Basis wären in diesem Fall die schon angesprochenen Closures, und die Diskussion daüber ob, die in Java eingeführt werden sollen, läuft schon ziemlich lange ziemlich kontrovers.... Wenn dir mal langweilig ist ... : Home - Parleys - Parleys.com - The Next Generation RIA eLearning Platform


----------



## knowledge (12. Mrz 2009)

>damit man sich die Zeile mit dem ActionListener spart". 

Das mein ich ja nicht, die würde man ja nicht sparen. Ich meine folgendes. Bisher:

xyz.add(ActionListener A) -> ruft in Klasse, welches das Listener Interface implementiert "actionPerformed" auf. Dort rufe ich dann, wenn nötig meine Methode(n) B auf. Schön wäre doch aber:

xyz.add(Klasse A, Methode B) -> ruft in Klasse A Methode B auf. Kein "Umweg" über actionPerformed. Was wäre an der Lösung unschön?


----------



## Marco13 (12. Mrz 2009)

Geht. Kannst du dir bauen. Überleg' dir, wie du die Methode beschreibst, wie du die Klasse beschreibst, wie du das Objekt beschreibst, und wie und wo du die Parameter für die Methode herbekommst, und verrühre das ganze mit ein bißchen Reflection. Fertig.

Aber auch der Event/Listener-Mechanismus ist ein sehr allgemeines Konzept. Diese Konzept ist

```
class Event { ...}
class Model { void addListener(Listener listener); }
interface Listener { void modelChanged(Event event); }
```
und dieses Konzept funktioniert für Buttons genauso wie für TreeSelections oder alles mögliche andere.

Um's zu hinterfragen:

```
class SomeClass
{
    void someMethod();
}

button.add( ... und was soll hier genau stehen ??? ... );
```
Wie auch immer die Antwort aussieht, man wird vermutlic immer sagen können, dass das 
1. Komplizierter ist, als mit einem ActionListener
2. Weniger mächtig ist, als mit einem ActionListener oder
3. Nicht durch die bestehenden sprachlichen Konzepte abgebildet werden kann


----------



## slawaweis (12. Mrz 2009)

knowledge hat gesagt.:


> Nur prinzipiell... Was wäre so schlimm gewesen, wenn man statt add(Listener) sowas wie add(Listener(MeineMethode) einführen würde, d.h. ich könnte bestimmen welche Methode (statt actionPerformed) aufgerufen werden könnte.


gar nichts. Aber was passiert, wenn jetzt jemand eine Methode, die 2 Parameter haben möchte, direkt aufrufen will, z.B. addListener(meine_methode(JButton button, int zustand))? Oder jemand will ein neues Objekt anlegen: addListener(list.add(new Integer(1)))? Oder wenn jemand ein Berechnung ausführen will: addListener(x += 5)?

Kurz gesagt, es gibt viele Möglichkeiten. Als Java damals entworfen wurde, kannte man zu der Zeit die Zeiger auf Funktionen aus C. Das hätte man irgendwie auch übernehmen können. Doch damals achtete man in Java strickt auf die *Typsicherheit*. In C ist es möglich per Zeiger irgendwelchen Unsinn als Parameter zu übergeben, welcher dann das Programm zu Absturz bringt. Damit in Java so was nicht passiert, musste überall ein klarer Typ hin. So ist ActionListener nichts anderes als eine Art Typ. Dieser Typ definiert ganz genau, was man einem AbstractButton anhängen kann und was man dann daraus bekommt, nämlich ein ActionEvent. Über das Interface ActionListener lässt sich dann die weitere Funktionalität umsetzen. Das soll Klarheit in das Programm bringen und zu mehr Sicherheit beitragen. Diese Sachen lernt man besonders dann gut zu schätzen, wenn man jahrelang in Java programmiert und Programm mit mehr als 100.000 Zeilen Quelltext geschrieben hat. Einfachheit oder Schnelligkeit beim Hintippen kann beim Programmieren oft ein Fluch sein, wenn man größere Sachen macht.

Weiterhin ist ActionListener eine Art Zwischenschicht. Damit lassen sich weit mehr Eigenschaften umsetzen, als das bloße Weiterleiten von Funktionsaufrufen. Zum einen kann man die Events filtern oder loggen, man kann die Benachrichtigung für eine Zeit ausschalten und vor allem müssen sich die Componente und der Zuhörer nicht direkt kennen, was dem MVC-Prinzip entspricht.



knowledge hat gesagt.:


> xyz.add(ActionListener A) -> ruft in Klasse, welches das Listener Interface implementiert "actionPerformed" auf. Dort rufe ich dann, wenn nötig meine Methode(n) B auf. Schön wäre doch aber:
> 
> xyz.add(Klasse A, Methode B) -> ruft in Klasse A Methode B auf. Kein "Umweg" über actionPerformed. Was wäre an der Lösung unschön?


wieder gar nichts, aber wie ich oben schrieb, jemand anderes will hier vielleicht z.B. xyz.add(Variable V, v += 5) stehen sehen. Es gibt vieles was man haben will und in Java kann man es sich selber bauen. Wenn man von JButton ableitet, kann man sich die Methode public void add(Object target, String method_name) implementieren und diese dann entsprechend realisieren. Oder man geht über eine Hilfsklasse mit statischen Methoden: public static void add(AbstractButton button, Object target, String method_name). So kann man sich so ein Verhalten selber zusammenstellen.

Slawa


----------



## knowledge (12. Mrz 2009)

>und vor allem müssen sich die Componente und der Zuhörer nicht direkt kennen, was dem MVC-Prinzip entspricht.

Ist es denn so großer Aufwand, wenn sich die Komponente und Zuhörer kennen? Ich mein was ist daran so schlimm?


----------



## SlaterB (12. Mrz 2009)

schau dir den JButton an, und eine neu definierte Controllerklasse X,
kennt JButton X? nein, es reicht wenn man ein Interface übergibt, einen ActionListener,
JButton kennt das Interface in aller denkbaren Ausführlichkeit, ohne OOP-Einbußen,
durch diesen Trick muss JButton nicht X kennen

eine allgemeine Library könnte man gar nicht anders programmieren

----

scheint mir nicht ganz mit dem Thema hier zu tun zu haben, aber soviel zu 'nicht kennen'


----------



## slawaweis (12. Mrz 2009)

knowledge hat gesagt.:


> >und vor allem müssen sich die Componente und der Zuhörer nicht direkt kennen, was dem MVC-Prinzip entspricht.
> 
> Ist es denn so großer Aufwand, wenn sich die Komponente und Zuhörer kennen? Ich mein was ist daran so schlimm?


ich versuche mal deine Aussage in anderen Worten wiederzugeben: "Wozu brauchen wir Telefone? Wir im Dorf können doch alle direkt mit einander reden". Wenn man anfängt in Java zu programmieren, dann hat man nur ein paar Buttons und ein paar Zeilen Code die darauf reagieren. Da ist die Welt noch ganz einfach.

Nehmen wir aber an, 100 Programmierer, verteilt über mehrere Orte, arbeiten an einer großen Anwendung mit einer aufwändigen GUI, wo hunderte von Funktion durch JButtons ausgelöst werden. Abhängig von der Darstellung, erscheinen verschiedenen Sets von JButtons zu selben Zeit (aber nie alle auf einmal). Weiterhin können durch Plugins weitere Funktion und somit auch JButtons in die Anwendung integriert werden. Hinzu kommt, dass die Funktionen nicht nur durch JButtons, sondern auch durch JMenuItems und Tastaturkürzel ausgelöst werden können. Würden sich jetzt alle JButton und Zuhörer direkt kennen, wäre da die Hölle los. So ist der ActionListener auch eine Abstraktionsschicht. Gäbe es so was nicht, müsste man es direkt erfinden. Java macht viele Kompromisse zwischen einfachen Einstieg und trotzdem mächtigen Werkzeugen für Profis. Ansonsten würde kein Mensch Java kennen und benutzen.

Slawa


----------



## Marco13 (12. Mrz 2009)

Die Frage

```
button.add( ... und was soll hier genau stehen ??? ... );
```
ist auch noch unbeantwortet. Ich wüßte spontan nicht, wie man das vernünftig machen sollte.


----------



## knowledge (13. Mrz 2009)

Danke für eure Antworten...

>So ist der ActionListener auch eine Abstraktionsschicht.

Dagegen ist ja nichts zu sagen. Ich wollte ja nur das man sowas wie JButton.add(ActionListener l) machen kann und in l kann ich dann "einstellen" welche Methode aufgerufen werden soll. D.h. statt actionPerformed() (default) dann halt eine andere beliebige Methode.

Und mir war/ist halt nicht klar, was daran so aufwendig sein soll...

@marco

add(ActionListener l). Der Listener bleibt, nur das man halt einstellen kann was für eine Methode aufgerufen wird (ausser actionPerformed)


----------



## Marco13 (13. Mrz 2009)

Ich meinte, wie dieser Aufruf tatsächlich aussehen soll. Wie soll man "einstellen", welche Methode aufgerufen wird? Über Strings?

button.add("somepackage.SomeClass", "someMethod");

Woher weiß man, auf welchem Objekt die Methode aufgerufen werden soll? Soll man das mit übergeben?

button.add("somepackage.SomeClass", theObject, "someMethod");

Wie ist sichergestellt, dass das Objekt zur Klasse passt? Oder soll NUR das Objekt und die Methode übergeben werden?

button.add(theObject, "someMethod");

Wie soll bei überladenen Methoden die richtige ausgewählt werden? Oder soll ein Method-Objekt übergeben werden, das man mit Reflection erhalten hat? 

button.add(theObject, theObject.getClass().getDeclaredMethod("theMethod", new Class[]{agumentType0,agumentType1,agumentType2}));

Wie sollen Argumente übergeben werden? Sollen die mit einer Ellipse einfach angehängt werden?

button.add(theObject, theObject.getClass().getDeclaredMethod("theMethod", new Class[]{agumentType0,agumentType1,agumentType2}), argument0, argument1, argument2);

Was soll mit den ganzen Exceptions passieren, die dort wegen reflection geworfen werden können? Was passiert, wenn sich die Signatur der Methode ändert? (Das bewirkt dann keinen Compilierfehler, sodern eine Exception zur Laufzeit!)

Und schließlich und endlich: Findest du 

button.add(theObject, theObject.getClass().getDeclaredMethod("theMethod", new Class[]{agumentType0,agumentType1,agumentType2}), argument0, argument1, argument2);

wirklich hübscher als

button.addActionListener(theActionListener); 

???


----------



## knowledge (13. Mrz 2009)

>Oder soll NUR das Objekt und die Methode übergeben werden?

Das wäre doch eine Möglichkeit, oder nicht?


----------



## Marco13 (13. Mrz 2009)

Die Fragen, die in den letzten beiden Dritteln meines Beitrages standen, hast du aber nicht beantwortet...


----------

