views:

641

answers:

7

I have a simple class - will call it Animal. I'd like to fire off an event in the Animal class and have it handled in the class where I instantiated the Animal class. In the event handler, I want to pass an Integer value

How do I pull off something simple like that?

+3  A: 

See java.util.Observable

EDIT: Adamski's PropertyChangeSupport based approach seems better Observable one that I suggested.

Hemal Pandya
My class already inherits from another class and I don't feel like adding a dummy in-between class just to get around the single inheritance limitation. There has to be a simpler way.
AngryHacker
I had wondered this may be the case. You are not the only one :-) see http://java.sun.com/j2se/1.4.2/docs/api/java/util/Observable.html. AspectJ based solution as Andrew mentions is an option. Other options are to incorporate the Observable pattern in your class Animal itself (it is not all that much code) or to contain and Observable inside Animal and delegate to it. For a reusable solution AspectJ seems to be the only choice.
Hemal Pandya
Use delegation.
Steve McLeod
+1  A: 

My first suggestion here would be to look at AspectJ. This is one of the design patterns that the language is best at handling. The following article provides a very eloquent description of how this can be implemented:

http://www.ibm.com/developerworks/java/library/j-aopwork6/index.html

Andrew Eisenberg
+4  A: 

When you want to avoid inheriting from a java.util.Observable-like base class, use an interface and let your observables implement or delegate the interface's methods.

Here is the observable interface:

public interface IObservable
{
        void addObserver(IObserver o);

        void deleteObserver(IObserver o);

        void notifyObservers(INotification notification);
}

Here is a helper class that could be used by your real observables:

import java.util.ArrayList;
import java.util.List;


public class Observable implements IObservable
{
        private List<IObserver> observers;

        @Override
        public synchronized void addObserver(IObserver o)
        {
                if (observers == null)
                {
                        observers = new ArrayList<IObserver>();
                }
                else if (observers.contains(o))
                {
                        return;
                }
                observers.add(o);
        }

        @Override
        public synchronized void deleteObserver(IObserver o)
        {
                if (observers == null)
                {
                        return;
                }
                int idx = observers.indexOf(o);
                if (idx != -1)
                {
                        observers.remove(idx);
                }
        }

        @Override
        public synchronized void notifyObservers(INotification notification)
        {
                if (observers == null)
                {
                        return;
                }
                for (IObserver o : observers)
                {
                        o.update(notification);
                }
        }

}

A real observable could look like this:

class Person implements IObservable
{
        private final IObservable observable = new Observable();

        @Override
        public void setFirstName(String firstName) throws Exception
        {
            if (firstName == null || firstName.isEmpty())
            {
                    throw new Exception("First name not set");
            }

            this.firstName = firstName;
            notifyObservers(new Notification(this, getFirstNamePropertyId()));
        }

    @Override
    public void addObserver(IObserver o)
    {
            observable.addObserver(o);
    }

    @Override
    public void deleteObserver(IObserver o)
    {
            observable.deleteObserver(o);
    }

    @Override
    public void notifyObservers(INotification notification)
    {
            observable.notifyObservers(notification);
    }

    private static final String FIRSTNAME_PROPERTY_ID = "Person.FirstName";

    @Override
    public String getFirstNamePropertyId()
    {
            return FIRSTNAME_PROPERTY_ID;
    }

}

Here is the observer interface:

public interface IObserver
{
        void update(INotification notification);
}

Finally, here is the notification interface and a basic implementation:

public interface INotification
{
        Object getObject();

        Object getPropertyId();
}

public class Notification implements INotification
{
        private final Object object;
        private final Object propertyId;

        public Notification(Object object, Object propertyId)
        {
                this.object = object;
                this.propertyId = propertyId;
        }

        @Override
        public Object getObject()
        {
                return object;
        }

        @Override
        public Object getPropertyId()
        {
                return propertyId;
        }
}
Frank Grimm
Holy crap. That's a lot of code to simply implement an event. I think I am gonna run back to C# :). Actually it does what I need, except that I'll need to convert it to pre-generics era (I am writing for the BlackBerry - they are still on a Java 1.3.
AngryHacker
Glad that the code helps you. When you consider that you can use the observable's code for all your observables, it's not so much code anymore. You can even cut down the notification code and just stick with java.lang.Objects.To convert the code to Java 1.3, I think you have to remove the @Override annotations and use Object-based lists/collections.
Frank Grimm
Your Observable helper class could throw a ConcurrentModificationException if a notified Observer attempts to remove itself during the notification callback. You could get round this by using a CopyOnWriteArrayList.
Adamski
@Adamski: Preventing ConcurrentModificationExceptions was the reason why I used synchronized for all of Observable's methods. And since all three synchronize blocks lock the Observable instance itself, the methods shouldn't be concurrently accessible.
Frank Grimm
A: 

There is also the great Spring libraries, that provide out-of-the-box an event framework.

KLE
+2  A: 

A simple event interface looks like this:

public interface AnimalListener {
    public void animalDoesSomething(int action);
}

Animal needs to manage its listeners:

public class Animal {
    private final List<AnimalListener> animalListeners = new ArrayList<AnimalListener>()
    public void addAnimalListener(AnimalListener animalListener) {
        animalListeners.add(animalListener);
    }
}

Your Animal-creating class needs to do this:

public class AnimalCreator implements AnimalListener {
    public void createAnimal() {
        Animal animal = new Animal();
        animal.addAnimalListener(this); // implement addListener in An
    }
    public void animalDoesSomething(int action) {
        System.ot.println("Holy crap, animal did something!");
    }
}

Now Animal can fire events.

public class Animal {
    ....
    public void doSomething() {
        for (AnimalListener animalListener : animalListeners) {
            animalListener.animalDoesSomething(4);
        }
    }
}

That looks like a lot of code for something as simple as “firing events” but maybe firing events isn’t simple at all. :)

Of course there are various extensions to this simple mechanism.

  • I always make my event listeners extend java.util.EventListener.
  • The first parameter for each listener method should be the source of the event, i.e. public void animalDoesSomething(Animal animal, int action);.
  • Management of registered listeners and event firing can be abstracted to some kind of abstract event listener management class. Look at PropertyChangeSupport to know what I mean.
Bombe
+1  A: 

The Observer/Observable classes have been in Java since day 1. Unfortunately the original designers screwed up somewhat. To be fair, they didn't have the chance to learn from 10 years of Java experience...

I solve your problem with delegation. I have my own implementation of Observer/Observable - and I recommend this. But here's an approach that works:

import java.util.Observable;
import java.util.Observer;

public class Animal {

    private final ImprovedObservable observable = new ImprovedObservable();

    public void addObserver(Observer o) {
        observable.addObserver(o);
    }

    public void notifyObservers() {
        observable.notifyObservers();
    }

    public void doSomething() {
        observable.setChanged();
        observable.notifyObservers(new AnimalEvent());
    }


}

// simply make setChanged public, and therefore usable in delegation
class ImprovedObservable extends Observable {
    @Override
    public void setChanged() {
        super.setChanged();
    }
}

class AnimalEvent {
}
Steve McLeod
+6  A: 

Assuming that the integer being passed is part of the Animal class state, an idiomatic way to do this rather than writing lots of your own code is to fire a PropertyChangeEvent. You can use the PropertyChangeSupport class to do this, reducing your code to this:

public class Animal {
  // Create PropertyChangeSupport to manage listeners and fire events.
  private final PropertyChangeSupport support = new PropertyChangeSupport(this);
  private int foo;

  // Provide delegating methods to add / remove listeners to / from the support class.  
  public void addPropertyChangeListener(PropertyChangeListener l) {
    support.addPropertyChangeListener(l);
  }

  public void removePropertyChangeListener(PropertyChangeListener l) {
    support.removePropertyChangeListener(l);
  }

  // Simple example of how to fire an event when the value of 'foo' is changed.
  protected void setFoo(int foo) {
    if (this.foo != foo) {
      // Remember previous value, assign new value and then fire event.
      int oldFoo = this.foo;
      this.foo = foo;
      support.firePropertyChange("foo", oldFoo, this.foo);
    }
  }
}

Finally, I would advise against using Observer / Observable as it makes code unreadable / difficult to follow: You are constantly having to check the type of the argument passed to the Observer using instanceof before downcasting it, and it's difficult to see what type of event a specific Observer implementation is expecting by looking at its interface definition. Much nicer to define specific listener implementations and events to avoid this.

Adamski