# Listener für Variable



## Frage (5. Sep 2007)

Gibt es in Java die Möglichkeit einen Listener für Variablen (boolsche Variablen) zu benutzen der anschlägt, wenn sich der Wert der Variable ändert oder muss man mit einer Endlosschleife immer abfragen?
(Variable aus anderer Klasse)


----------



## Wildcard (5. Sep 2007)

Dafür verwendet man das Observer Pattern. Sprich: Diesen Listener musst du selbst schreiben.


----------



## Frage (5. Sep 2007)

so, jetzt muss ich n kurzes Lob an dich und ans Forum loswerden.
Echt cool, so schnell eine Antwort zu haben und die Tatsache dass man im Forum dann auch schnell eine detailierte Beschreibung für das Listener schreiben findet, finde ich echt gut!

jetzt muss ich nur noch probieren ob ich das auch hinbekomm...


----------



## Wildcard (5. Sep 2007)

Im Prinzip ist die Sache ganz einfach. Du kannst auch Klassen wie zB das AbstractTableModel als Vorbild nehmen, da sie im Prinzip nicht viel anderes macht als Events zu feuern.


----------



## SlaterB (5. Sep 2007)

also das heißt (edit: bezogen auf erste Antwort von Wildcard), dass man den Listener nicht an die boolsche Variable hängt,
sondern an ein Objekt einer eigenen Klasse ObservedBoolean oder ähnliches,

dann kann man für Benachrichtigung sorgen:

privale boolean value;

public void setValue(boolean newValue) {
this.value = newValue;
// inform all registered Listeners
}


----------



## Wildcard (5. Sep 2007)

SlaterB hat gesagt.:
			
		

> also das heißt, dass man den Listener nicht an die boolsche Variable hängt,
> sondern an ein Objekt einer eigenen Klasse ObservedBoolean oder ähnliches,


Oder, je nach Anwendungsfall, an die Klasse die das Attribut enthält. Im setter wird dann das Event gefeuert.


----------



## Frage (5. Sep 2007)

Ich hab jetzt zwar noch nicht alles verstanden was ihr grad gesagt habt, aber ich les grad au noch ein paar Sachen zu den Listenern durch..

nur nochmal kurz wie ich es mir vorgestellt hab.

```
AndereKlasse.boolscheVar.addBoleanListener(this);

&

public void booleanValueChanged(BooleanValueChanged e){
}
```
aber das sind wie gesagt, nur die Gedanken von jemand der noch keine Ahnung hat


----------



## Frage (5. Sep 2007)

Also ich hab mir jetzt mal hier gelesen und des so mehr oder weniger abgeschrieben wie ichs verstanden hab und wie ichs für einen BooleanValueListener wohl brauche:


```
public interface BooleanValueListener {
    public abstract booleanValueChanged(ChangeEvent);
}
```


```
public ChangeEvent {
  public ChangeEvent() {

  }
}
```


```
public class ObservedBoolean {

    private Vector changeRecipients; // die Liste der Listener!

    public ObservedBoolean() {
        changeRecipients = new Vector();
    }

    public void addBooleanValueListener(BooleanValueListener listener) {
        changeRecipients.add(listener);
    }

    public void removeBooleanValueListener(BooleanValueListener listener) {
        changeRecipients.remove(listener);
    }

    public void fireChangeUpdate(ChangeEvent chEv) {
        Iterator recIt = changeRecipients.iterator();

        while(recIt.hasNext()) {
            ((ChangeListener)recIt.next()).changeHappened(chEv);
        }
    }
}
```


Mit anderen Worten ich habs nich richtig verstanden


----------



## SlaterB (5. Sep 2007)

was hast du nicht verstanden? sieht doch schon gut aus, 
fehlt natürlich noch der boolean, die set-Operation und jemand der sich als Listener registeriert,

wenn du aber z.B. noch nie einen ActionListener bei einem Button eingesetzt hast,
dann ist das Prinzip wirklich nicht leicht zu verstehen


----------



## Guest (5. Sep 2007)

```
public class ObservedBoolean { 

    private Vector changeRecipients; // die Liste der Listener! 
    private boolean b;

    public ObservedBoolean(boolean b) { 
        changeRecipients = new Vector(); 
        this.b = b;
    } 

    public void addBooleanValueListener(BooleanValueListener listener) { 
        changeRecipients.add(listener); 
    } 

    public void removeBooleanValueListener(BooleanValueListener listener) { 
        changeRecipients.remove(listener); 
    } 

    public void fireChangeUpdate(ChangeEvent chEv) { 
        Iterator recIt = changeRecipients.iterator(); 

        while(recIt.hasNext()) { 
            ((ChangeListener)recIt.next()).changeHappened(chEv); 
        } 
    } 
}
```

Hast du sowas mit dem fehlenden boolean gemeint?
Die set  Methode sagt mir jetzt nichts.
Und jemand der sich als Listener registriert...?

Was muss ich den später in einer Klasse in der ich den Listener verwenden will alles impoertieren?


----------



## SlaterB (6. Sep 2007)

die set-Operation habe ich oben schon mal beschrieben,

importieren musst du in allen Java-Klassen der Welt genau die Klassen, die du verwendest


----------



## Frage (6. Sep 2007)

ich hab jetzt die 3 Klassen nochmal verändert und in ein package getan.


```
package BooleanListener;

import java.util.EventListener;

public interface BooleanListener extends EventListener {
  public abstract void booleanValueChanged(ChangeEvent e);
}
```


```
package BooleanListener;

public class ChangeEvent {
  boolean b;

  public ChangeEvent(boolean b) {
    this.b = b;
  }
  
  public boolean getValue(){
    return b;
  }
}
```


```
package BooleanListener;

import java.util.Vector;
import java.util.Iterator;

public class ObservedBoolean {

    private Vector changeRecipients;
    private boolean b;

    public ObservedBoolean(boolean b) {
        changeRecipients = new Vector();
        this.b = b;
    }

    public void addBooleanListener(BooleanListener listener) {
        changeRecipients.add(listener);
    }

    public void removeBooleanListener(BooleanListener listener) {
        changeRecipients.remove(listener);
    }

    public void fireChangeUpdate(ChangeEvent chEv) {
        Iterator recIt = changeRecipients.iterator();

        while(recIt.hasNext()) {
            ((BooleanListener)recIt.next()).booleanValueChanged(chEv);
        }
    }
    
    public void setValue(boolean newValue){
      this.b = newValue;
    }
}
```

Wenn ich das letztere allerdings compilieren will, kommt die Meldung:
Note: ObservedBollean.java uses unchecked or unsafe Operations.
Note: Recompile with -Xlint:unchecked for details.

(was ist Xlint:unchecked? ...Google brachte nichts)


Unter der Annahme das das mit dem Compilieren nun gegangen wäre, ist der Listener jetzt soweit in Ordung, dass man ihn etwa so verwenden könnte:


```
ObservedBoolean b1 = new ObservedBoolean();
ObservedBoolean b2 = new ObservedBoolean();
b1.addBooleanListener(this);
b2.addBooleanListener(this);

public void booleanValueChanged(ChangeEvent e){
  if(e.getSource().equals(b1)) System.out.println("changed: " + b1);
  if(e.getSource().equals(b2)) System.out.println("changed: " + b2);
}
```

?


----------



## SlaterB (6. Sep 2007)

das ChangeEvent solltest du leer lassen, dessen Aufgabe ist nur, zu übermitteln, dass sich was getan hat,
allein dass ein Event kommt, reicht

getSource() wäe tatsächlich eine passende Erweiterung, die hat den ChanceEvent noch nicht!

um dann den aktuellen Wert zu bekommen müsste man bei getSource() den Value nachfragen
so würde das eine ChangeEven in beliebigen Oberserved funktionieren, auch ObserveredString, ObserveredInt usw.
--------

> public void setValue(boolean newValue){ 
>       this.b = newValue; 
>    } 

wo werden hier die Listener über eine Änderung informiert?
welche bereits vorhandene Operation musst du hier wohl aufrufen, damit die Informierung ihren Lauf nimmt?


----------



## Wildcard (6. Sep 2007)

Das ist lediglich eine Warnung das du keine Generics verwendest, die Liste also nicht typsicher ist. Das kannst du ignorieren, oder Generics einbauen. Die Funktionalität ist davon nicht beeinträchtigt.


```
public void setValue(boolean newValue){
      this.b = newValue;
    }
```
Hier müsstest du noch die fire Methode aufrufen um die Listener zu benachrichtigen (und eventuell vorher überprüfen ob nicht versucht wird den gleichen Wert erneut zu setzen, dann braucht man nämlich kein Event zu feueren.


----------



## Frage (6. Sep 2007)

Also nochmal überarbeitet:


```
package BooleanListener;

import java.util.EventListener;

public interface BooleanListener extends EventListener {
  public abstract void booleanValueChanged(ChangeEvent e);
}
```


```
package BooleanListener;

public class ChangeEvent {
  protected Object source;

  public ChangeEvent(Object obj) {
    source = obj;
  }
  
  public Object getSource() {
    return source;
  }
}
```


```
package BooleanListener;

import java.util.Vector;
import java.util.Iterator;

public class ObservedBoolean {

    private Vector<BooleanListener> changeRecipients;
    private boolean b;

    public ObservedBoolean(boolean b) {
        changeRecipients = new Vector<BooleanListener>();
        this.b = b;
    }

    public void addBooleanListener(BooleanListener listener) {
        changeRecipients.add(listener);
    }

    public void removeBooleanListener(BooleanListener listener) {
        changeRecipients.remove(listener);
    }

    public void fireValueUpdate(ChangeEvent chEv) {
        Iterator recIt = changeRecipients.iterator();

        while(recIt.hasNext()) {
            ((BooleanListener)recIt.next()).booleanValueChanged(chEv);
        }
    }
    
    public void setValue(boolean newValue){
      if(b != newValue){
        this.b = newValue;
        fireValueUpdate(new ChangeEvent(newValue));
      }
    }
}
```



Möglicher aufruf:

```
ObservedBoolean b1 = new ObservedBoolean(); 
ObservedBoolean b2 = new ObservedBoolean(); 
b1.addBooleanListener(this); 
b2.addBooleanListener(this); 

public void booleanValueChanged(ChangeEvent e){ 
  if(e.getSource().equals(b1)) System.out.println("changed: " + b1); 
  if(e.getSource().equals(b2)) System.out.println("changed: " + b2); 
}
```

lässt sich diesmal zumindest alles compilieren


----------



## SlaterB (6. Sep 2007)

> new ChangeEvent(newValue)

source ist also ein Boolean, true oder false?..

besser new ChangeEvent(this), denn this ist der Button,

noch besser ist in diesem Fall vielleicht fireValueUpdate(); ohne Parameter,
das simple new ChangeEvent(this) kann  fireValueUpdate(); selber erstellen


-----

da das Event so allgemein ist, könnte der Listener übrigens auch ganz allgemein
ChangeListener oder so heißen, muss nicht nur für Boolean ausgelegt sein


----------



## Guest (6. Sep 2007)

SlaterB hat gesagt.:
			
		

> > new ChangeEvent(newValue)
> 
> source ist also ein Boolean, true oder false?..
> 
> ...



> source ist also ein Boolean, true oder false? ...
ich versteh nich ganz was du damit meint. Dass ich source nicht als Object sondern als boolean deklarisieren soll? Ich hab halt gedacht sie Quelle is ja ein Objekt:"ObservedBoolean"

fireValueUpdate ohne parameter geht nicht...
ich nehm mal an du meintest statt

fireValueUpdate(new ChangeEvent(...)) soll ich einfaches n neues ChangeEvent(ChangeEvent c = new ChangeEvent...?) erzeugen was dann von selbst das fireValueUpdate aufruft...?

Sorry für die vielen Fragen, aber ich bin halt neu in Java und hab auch sonst keine Programmiererfahrung...


----------



## SlaterB (6. Sep 2007)

> Ich hab halt gedacht die Quelle is ja ein Objekt:"ObservedBoolean" 

genau das Objekt ist es nicht, sondern du übergibst newValue, also true/ false,
übergebe this, denn this ist das ObservedBoolean

----------
> fireValueUpdate ohne parameter geht nicht... 


```
fireValueUpdate(new ChangeEvent(newValue)); 
+
public void fireChangeUpdate(ChangeEvent chEv) { 
  ..
}
```
wird zu

```
fireValueUpdate(); 
+
public void fireChangeUpdate() { 
  ChangeEvent chEv = new ChangeEvent(this)
  ..
}
```


----------



## Frage (6. Sep 2007)

ok das hab ich jetzt.

ich hab nun versucht ein Testprogramm zu schreiben, aber:


```
import BooleanListener.*;

public class test{
  ObservedBoolean b1 = new ObservedBoolean(true);
  b1.addBooleanListener(this);
  
  ObservedBoolean b2 = new ObservedBoolean(true);
  b2.addBooleanListener(this);
  
  public static void main(String args[]){
    b1.setValue(false);

    //...
    b2.setValue(false);

    //...
  }
  
  public void booleanValueChanged(ChangeEvent e){
    if(e.getSource().equals(b1)) System.out.println("1");
    if(e.getSource().equals(b2)) System.out.println("2");
  }
}
```

lässt sich nicht kopieren, da bei addBooleanListener jeweils ein <identifier> expected wird. Das ein Kistener schreiben solang dauern kann ... *müdegrins*


----------



## SlaterB (6. Sep 2007)

public class test implements BooleanListener {
..
}

der Code muss außerdem in eine Operation/ einen Konstruktor,
man kann nicht Code direkt in eine Klasse schreiben (bis auf Variablendeklarationen)


----------



## Guest (6. Sep 2007)

uupps ja...

und das implements vergessen... ich werd wohl allmählich wirklich müde...

danke für deine ganze hilfe slater!


----------



## chilla (6. Sep 2007)

als neuling ein listener managment aufzuziehen (sei es auch noch so simpel gehalten) ist wirklich keine triviale sache.

dann auch noch in so kurzer zeit.. echt fein 


 :toll:  :toll:  :toll:


----------



## Frage (9. Sep 2007)

Hi!

Ich bräuchte schon wieder Hilfe mit einem Listener :-(

Und zwar habe ich nun eine Instanz einer Klasse MathVector und bräuchte nun einen Listener der auf eine Änderung von mathVetor.getNorm() reagiert und irgendwie hab ichs bisher nicht geschafft so einen selber zu schreiben :-(

Ich hab mehrere Sachen probiert, ich stell hier mal kurz meinen letzten Versuch rein:


```
package ValueListener;

import java.util.EventListener;

public interface ValueListener extends EventListener {
  public abstract void valueChanged(ChangeEvent e);
}
```


```
package ValueListener;

public class ChangeEvent {
  protected Object source;

  public ChangeEvent(Object obj) {
    source = obj;
  }
  
  public Object getSource() {
    return source;
  }
}
```


```
package ValueListener;

import java.util.Vector;
import java.util.Iterator;
import MathVec.MathVector;

public class ObservedVector extends MathVector{

  private Vector<ValueListener> changeRecipients;
  private int dirX;
  private int dirY;
  private double norm;

  public ObservedVector(MathVector vector) {
    changeRecipients = new Vector<ValueListener>();
    this.dirX = vector.getDirX();
    this.dirY = vector.getDirY();
    this.norm = vector.getNorm();
  }
  
  public ObservedVector(int dirX, int dirY) {
    super(dirX, dirY);
  }
  
  public void addValueListener(ValueListener listener) {
    changeRecipients.add(listener);
  }

  public void removeValueListener(ValueListener listener) {
    changeRecipients.remove(listener);
  }

  public void fireValueUpdate() {
    ChangeEvent chEv = new ChangeEvent(this);
    Iterator recIt = changeRecipients.iterator();

    while(recIt.hasNext()) {
      ((ValueListener)recIt.next()).valueChanged(chEv);
    }
  }
  
  public void setValue(double newNorm){
    if(getNorm() != newNorm){
      setNorm(newNorm);
      fireValueUpdate();
    }
  }
}
```

Also die klasse an ders hängt ist ObservedVector, ich denke nicht dass die andern 2 falsch sind (oder...?).
Ich hoff hier kann mir jemand helfen!


----------



## SlaterB (9. Sep 2007)

was ist jetzt anders als bei dem boolean,
eine set()-Operation schreiben und darin fireValueUpdate() aufzurufen?


----------



## Frage (9. Sep 2007)

habe ich ja geschrieben... nur anscheinend falsch, da es nicht funktioniert


----------



## Frage (9. Sep 2007)

Also ich hab jetzt weiterprogrammiert und das mal weggelassen und stehe nun vor einem ähnlichen Problem. 
Ich beschreib es jetzt einfach mal vereinfacht und wie ich mir einen möglichen Lösungsweg vorstelle (so mehr oder weniger):

Ich habe eine Klasse die ein Component (ich nenn es hier mal DrawComponent) erzeugt, auf dem der Benutzer eine Linie malen kann / soll und eine Klasse die ein Component (ich nenn es hier mal DisplayComponent) erzeugt, das nachher die Länge ausgeben soll. Die Eingabe einer Länge ist hier auch möglich wobei sich dann wieder die Linienlänge des Malfeldes ändern soll. Beide Klassen verfügen über jeweils eine set und get Methode um die aktuelle Länge zu erhalten und einzustellen. Jetzt muss ich halt noch die Interaktion zwischen beiden regeln. Ich bin der Ansicht das hier wohl auch das Observer Pattern verwenden muss.

Jetzt habe ein Interface eines Listener, ein einfaches Event und brauche nun noch das Objekt, welches den Listener erhält.
Für dieses Objekt schreibe ich eine Unterklasse zu DrawComponent und eine Unterklasse zu DisplayComponent, die dann alle Methoden etc erben und füge noch die Methoden: addListener, removeListener, fireValueUpdate, und setValue (in der ich dann fireValueUpdate aufrufe) hinzu.
Nun verwende ich bei der erstellung meines GUIs einfach die Unterklassen von DrawComponent und DisplayComponent, statt ebendiesen beiden.

So in etwa stell ichs mir vor, stimmt das soweit?
Wenn ja: Ich versteh die setValue Methode nicht ganz. Letzendlich soll fireValueUpdate ja aufgerufen werden, sobald sich zB ObservedDrawComponent.getLength() ändert.
Funktioniert das ganze dann wenn die Methode so aussieht:


```
double length = getLength();

public void setValue(double newLength){
  if(length != newLength){
    setLength(newLength);
    fireValueUpdate();
  }
}

Ich frage das ganze weil ich heut nun schon ewig an dem (und dem oben genannten Problem sitze und es bisher noch nicht funktioniert.
Danke für jede Hilfe!
```


----------



## SlaterB (9. Sep 2007)

> habe ich ja geschrieben... nur anscheinend falsch, da es nicht funktioniert

?
1. ist im obigen Post nix von setValue() zu sehen, und
2. wenn du damit Probleme hast, dann musst du die beschreiben..

> So in etwa stell ichs mir vor, stimmt das soweit? 

ich habe in 5 Jahren das System 1-2x eingesetzt, du 2x innerhalb einer Woche,
wahrscheinlich alles unnötig

> Ich versteh die setValue Methode nicht ganz. 

tolle Problembeschreibung..

> Funktioniert das ganze dann wenn die Methode so aussieht: 

so was zu fragen ist nicht völlig falsch, aber ich schlage dir mal was effives vor:
du programmierst alleine und wenn was nicht geht, dann sagst du Bescheid
und benennst genau die Probleme


----------



## Guest (10. Sep 2007)

SlaterB hat gesagt.:
			
		

> ich habe in 5 Jahren das System 1-2x eingesetzt, du 2x innerhalb einer Woche,
> wahrscheinlich alles unnötig



DerMeinung bin ich auch deshalb hab ich in meinem Post vorher ja beschrieben was ich programmieren will. Wenns da ne andere Möglichkeit gibt wär ich froh...


zu setValue() nochmal ich meinte die Methode in dem letzten Code 2 Beiträge von mir hier drüber in Z. 42

Und ein Problem näher zu beschreiben ist halt schwierig wenn man codet etc compilieren geht nur der Listener funktioniert nicht so wie man es erwartet. Ich hab ja nicht gleich hier ins Forum geschrieben, sondern erst mal noch 2 stunden rumprogramiert und im INeT gesucht. Hier hab ich das ganze halt nochmal geschrieben in der Hoffnung das euch spontan was auffällt oder so..

naja ich seh ein das man mit der Problembeschreibung schwer helfen kann, auch wenn ich nicht in der Lage bin es grad besser zu beschreiben.


----------



## SlaterB (10. Sep 2007)

> zu setValue() nochmal ich meinte die Methode in dem letzten Code 2 Beiträge von mir hier drüber in Z. 42 

wenn die schon immer da war, dann bin ich blind, sorry,
dadrin wird getNorm() benutzt, ist das überhaupt definiert?

und so schwer kann das ja nicht zu verfolgen sein,
überall System.out.prinltn() a la
'Wert geändert',
'informiere x Listener'
'Listener y enthält Event'
usw,
dann siehst du ja, wo die Liste unterbrochen ist


----------



## Guest (10. Sep 2007)

ah danke, auf so ne überprüfung wär ich gar nich gekommen


----------

