24

I am learning Java and I want to make my class into an observable class.

However I already have it extending another class.

What should I do?

рüффп
  • 5,172
  • 34
  • 67
  • 113
unj2
  • 52,135
  • 87
  • 247
  • 375

6 Answers6

41

I'd recommend avoiding using the Observable class altogether, but rather define event-specific listeners and corresponding event definitions. Then define a list of listeners within your class along with methods to add and remove listeners, and propagate events to them (see below).

Observable forces you to use java.lang.Object to represent events and then check the event type using instanceof, which is an ugly non-OO approach, and makes the code more difficult to understand. If you look at the classes within the javax.swing package you'll see they avoided using Observer / Observable altogether and used an approach similar to the below.

Event Definition

public class MyChangeEvent extends EventObject {
  // This event definition is stateless but you could always
  // add other information here.
  public MyChangeEvent(Object source) {
    super(source);
  }
}

Listener Definition

public interface MyChangeListener {
  public void changeEventReceived(MyChangeEvent evt);
}

Class Definition

public class MyClass {
  // Use CopyOnWriteArrayList to avoid ConcurrentModificationExceptions if a
  // listener attempts to remove itself during event notification.
  private final CopyOnWriteArrayList<MyChangeListener> listeners;

  public class MyClass() {
    this.listeners = new CopyOnWriteArrayList<MyChangeListener>();
  }

  public void addMyChangeListener(MyChangeListener l) {
    this.listeners.add(l);
  }

  public void removeMyChangeListener(MyChangeListener l) {
    this.listeners.remove(l);
  }

  // Event firing method.  Called internally by other class methods.
  protected void fireChangeEvent() {
    MyChangeEvent evt = new MyChangeEvent(this);

    for (MyChangeListener l : listeners) {
      l.changeEventReceived(evt);
    }
  }
}
zyamys
  • 1,609
  • 1
  • 21
  • 23
Adamski
  • 54,009
  • 15
  • 113
  • 152
  • 1
    java.util.Observable also has a flag to indicate whether anything has changed. And sometimes a new Event class is overkill. – finnw Nov 01 '09 at 23:18
  • @finnw - Most of my event class definitions are < 10 lines of autogenerated code and take seconds to write. However, in situations where I wish to avoid defining new event classes I favour PropertyChangeEvent / PropertyChangeListener / PropertyChangeSupport where at least you can inspect the property name before attempting any casts. – Adamski Nov 01 '09 at 23:21
  • CopyOnWriteArrayList is unnecessary - just explicitly copy on read or write. `PropertyChangeListener` is a bit of a mess. `javax.swing.event.ChangeListener` is a clean, something just changed. In fact ignoring event objects makes things even cleaner (in reality they don't carry useful information for change events, as opposed to input events). – Tom Hawtin - tackline Nov 01 '09 at 23:36
  • 1
    CopyOnWriteArrayList is needed unless you want to synchronize the fireChangeEvent, addMyChangeListener, and removeMyChangeListener methods or get a java.util.ConcurrentModificationException if you add/remove while the for loop is running. If the list isn't frequently added/removed from (and probably isn't in most cases) the COpyOnWriteArrayList is probably better. – TofuBeer Nov 01 '09 at 23:52
  • 1
    +1 for CopyOnWriteArrayList. Getting reentrant semantics right is a pain without it and it's more efficient than always copying. – Dave Ray Nov 02 '09 at 00:22
  • I'm not sure it is more efficient than always copying (even on read). I'm pretty sure it doesn't make many difference in terms of CPU cycles, but will use more memory and therefore more GC cycles. If you copy on read you also get to spend cycles at non-critical times. – Tom Hawtin - tackline Nov 02 '09 at 04:52
  • Personally I'd go with synchronized and be done with it. No copying and only really a performance problem if things are happening with a high frequency. – TofuBeer Nov 02 '09 at 06:39
  • @Tom: I don't understand the argument in copying the list for all reads and writes where, with CopyOnWriteArrayList you have a more efficient implementation in that the list is only copied when a listener is added or removed, but not when firing an event (likely to be the common case). – Adamski Nov 02 '09 at 08:10
  • 1
    @TofuBeer: synchronizing on all methods will not work: If a call is made to fireChangeEvent() and a listener is notified and attempts to remove itself this will still cause a ConcurrentModificationException because the thread doing the notifying *already holds the object lock* and hence can call removeChangeListener *without blocking*. – Adamski Nov 02 '09 at 08:11
  • @Tom: I agree with you re. ChangeListener in that it's cleaner / simpler - I use this where possible but am often dealing with complex UI panels where where PropertyChangeListener is more useful. Also, if I bind to a specific property name it's a lot clearer in the code IMHO. – Adamski Nov 02 '09 at 08:14
  • `CopyOnWriteArrayList` was quite clearly written for *exactly this sort of thing*. Adding listeners is infrequent and yet it provides safe iteration and removes the need to have complex logic to handle the case where the processing of an event by the listener attempts to add/remove listeners – oxbow_lakes Nov 02 '09 at 08:47
  • @Adamski I've been programming in Java since 1995 and have yet to see a real world case where that happens... in face I would consider the code broken if it tried to do that... but yes it could. – TofuBeer Nov 03 '09 at 23:49
  • @TofuBeer: For the sake of typing CopyOnWriteArrayList rather than ArrayList I would say it's worth avoiding the risk that it may happen. I agree that it's a rare event but we have seen it in applications that dynamically add / remove many listeners over the application's life-time. – Adamski Nov 03 '09 at 23:58
  • In the MyChangeEvent class, "super(this);" should be "super(source);" – Jeremy Ross Dec 16 '09 at 16:23
  • @Adamski Are you intentionally nesting the class definition of MyClass or is that a mistake? – user2303321 Aug 29 '18 at 19:07
1

Java doesn't allow multiple inheritance, so there is no direct way to do it. You should consider using a delegate pattern having your main object that delegates his observer behaviour to an another object..

class YourObject extends ItsAncestorClass
{
      private Observer includedObserver;

      public Observer getHisObserver(..)
}

Another approach would be turning the object from which your class is extending to an interface, then you'll be allowed to extend from Observer.

Jack
  • 131,802
  • 30
  • 241
  • 343
1

Another option is to wrap your object in an Observable object.

public class MyObjectObservableWrapper implements Observable {  
  private MyObject myObject;
  public MyObjectObservaleWrapper(MyObject myObject){
    this.myObject = myObject;
  }
  // interface methods here
}

This option works when the data to be used by the Observable methods is accessible through public methods of MyObject. So, it may not be suitable for all cases.

Chuck Deal
  • 81
  • 1
  • 5
0

Multiple inheritance (to extend two classes) is not possible in Java. Is considered a bad design in almost all cases.

If you give us some more information, maybe somebody can help you a little more.

sinuhepop
  • 20,010
  • 17
  • 72
  • 107
0

You can extend Observable and wrap the original parent in your to-be observable child class. Delegate all method calls to the wrapped object.

-6

Use a bridge.

Create a class that extends Observable that the first class just calls the methods of the second class.

Bridge method detail:

 public class XXX {
    public class XXXObservableBridge : Observable {
       public void RaiseEvent();
       // Listeners etc
    }

    private XXXObservableBridge ObservableBridge;

    XXX() {
       ObservableBridge = new ObservableBridge;
    }

    public Observable AsObservable() { return ObservableBidge; }

    public void RaiseEvent() { ObservableBridge.RaiseEvent(); }
 }
Joshua
  • 40,822
  • 8
  • 72
  • 132
  • 3
    -1 this is a Java question, not C#. Please stick to Java syntax and APIs – finnw Nov 01 '09 at 23:16
  • @finnw C# has no need for the observable/observer pattern and my code doesn't even call any API functions. – Joshua Nov 02 '09 at 00:41
  • 1
    There isn't an IObservable in Java, and even if you defined one, java.util.Observable does not implement it. – Stephen C Nov 02 '09 at 04:10