Как объявлять и использовать события в Java

У меня простой класс-назовем его животным. Я хотел бы запустить событие в классе животных и обработать его в классе, где я создал экземпляр класса животных. В обработчике событий я хочу передать целое значение

Как я могу сделать что-то простое, как это?

9 ответов


когда вы хотите избежать наследования от java.утиль.Observable-как базовый класс, используйте интерфейс и позвольте вашим наблюдаемым реализовать или делегировать методы интерфейса.

вот наблюдаемый интерфейс:

public interface IObservable
{
        void addObserver(IObserver o);

        void deleteObserver(IObserver o);

        void notifyObservers(INotification notification);
}

вот вспомогательный класс, который может использоваться вашими реальными наблюдаемыми:

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);
                }
        }

}

реальный наблюдаемый может выглядеть так:

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;
    }

}

вот интерфейс наблюдателя:

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

наконец, вот интерфейс уведомлений и базовая реализация:

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;
        }
}

предполагая, что передаваемое целое число является частью состояния класса Animal, идиоматический способ сделать это вместо того, чтобы писать много собственного кода - это огонь PropertyChangeEvent. Вы можете использовать PropertyChangeSupport класс для этого, уменьшая ваш код до этого:

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);
    }
  }
}

наконец, Я бы не советовал использовать Observer / Observable поскольку это делает код нечитаемым / трудным для отслеживания: вам постоянно приходится проверять тип аргумента, переданного Observer используя instanceof перед его понижением, и трудно увидеть, какого типа события ожидает конкретная реализация наблюдателя, посмотрев на его определение интерфейса. Гораздо лучше определить конкретные реализации прослушивателя и события, чтобы избежать этого.


простой интерфейс событий выглядит так:

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

Animal необходимо управлять своими слушателями:

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

код Animal-создание класса должно сделать это:

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!");
    }
}

теперь Animal может пожары.

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

это похоже на много кода для чего-то простого, как "события стрельбы", но, возможно, события стрельбы совсем не просты. :)

конечно, есть различные расширения для этого простого механизм.

  • я всегда делаю мои слушатели продлить java.util.EventListener.
  • первый параметр для каждого метода прослушивателя должен быть источником события, т. е. public void animalDoesSomething(Animal animal, int action);.
  • управление зарегистрированными слушателями и запуск событий можно абстрагировать в какой-то абстрактный класс управления прослушивателем событий. Посмотреть PropertyChangeSupport чтобы знать, что я имею в виду.

посмотреть java.util.Observable

EDIT: Адамски PropertyChangeSupport основанный подход кажется лучше наблюдаемым, который я предложил.


Я считаю, что самое простое решение из них было пропущено немного...

вам не нужно больше, чем это в 95% случаев:

public class Aminal extends Observable {
    public void doSomethingThatNotifiesObservers() {
        setChanged();
        notifyObservers(new Integer(42));
    }
}

Я предполагаю, что у вас не будет автоматического бокса, поэтому я сделал Integer объект, но часть от этого,Observable класс из JDK1.0 поэтому он должен присутствовать в Вашей версии Java.

С автоупаковка (в Java 1.5 и позже)notifyObservers вызов будет выглядеть так:

notifyObservers(42);

для того, чтобы поймать отправленное событие необходимо для реализации Observer интерфейс:

public class GetInt implements Observer {
    @Override
    public void update(Observable o, Object arg) {
        if (arg instanceof Integer) {
            Integer integer = (Integer)arg;
            // do something with integer
        }
    }
}

тогда вы добавите GetInt до Animal класс:

Animal animal = new Animal();
GetInt getint = new GetInt();
animal.addObserver(getint);

это все стандартные Java и Observable класс реализует всю необходимую обработку наблюдателя.

Если вам нужно быть в состоянии вызвать Observable извне перейдите к решению Стива Маклеода, но по моему опыту вы захотите, чтобы ваш класс обрабатывал то, что он знает, и позволял другим классам взаимодействовать с ним через Observer интерфейсы.

единственное, что вам нужно знать, это то, что вам нужно позвонить setChanged() до notifyObservers() или Observable не будет отправлять никаких событий.

Я думаю, это так, что вы можете назвать несколько setChanged() а затем уведомить наблюдателей только один раз, давая им знать, что класс изменился и оставляя им решать, как это сделать.

это также хорошая форма, чтобы удалить наблюдателя, как только класс, где он был создан, больше не нужен, это для предотвращения наблюдатель, оставленный в наблюдаемом. Просто мусор, собирающий класс observer, не удалит его из наблюдаемого.

если вы хотите наблюдать за классом на основе каждого свойства, я бы рекомендовал пойти с PropertyChangeSupport как описано выше Адамский. Есть также VetoableChangeSupport это можно использовать, если вы хотите, чтобы слушатели могли блокировать изменения.


классы Observer / Observable были в Java с 1-го дня. К сожалению, оригинальные дизайнеры немного облажались. Честно говоря, у них не было возможности учиться на 10-летнем опыте Java...

Я решаю вашу проблему с делегированием. У меня есть собственная реализация Observer/Observable - и я рекомендую это. Но вот подход, который работает:

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 {
}

моим первым предложением здесь было бы посмотреть AspectJ. Это один из шаблонов проектирования, который язык лучше всего обрабатывает. Следующая статья дает очень красноречивое описание того, как это может быть реализовано:

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


есть также Великий Весна библиотеки, которые предоставляют из коробки база событий.


гуава это EventBus еще одна готовая альтернатива