如何在Java中声明和使用事件

时间:2009-10-12 06:26:22

标签: java events event-handling

我有一个简单的类 - 将其称为Animal。我想在Animal类中触发一个事件,并在我实例化Animal类的类中处理它。在事件处理程序中,我想传递一个Integer值

如何实现这样简单的事情?

9 个答案:

答案 0 :(得分:21)

假设传递的整数是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 ,因为它会使代码难以理解/难以理解:您经常需要检查传递的参数的类型在向下转换之前使用Observerinstanceof,并且通过查看其接口定义很难看出特定Observer实现期望的事件类型。更好地定义特定的侦听器实现和事件以避免这种情况。

答案 1 :(得分:9)

如果要避免从类似java.util.Observable的基类继承,请使用接口并让您的observable实现或委托接口的方法。

这是可观察的界面:

public interface IObservable
{
        void addObserver(IObserver o);

        void deleteObserver(IObserver o);

        void notifyObservers(INotification notification);
}

这是一个可以由你的真正的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);
                }
        }

}

真实的观察者看起来像这样:

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

答案 2 :(得分:5)

简单的事件界面如下所示:

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以了解我的意思。

答案 3 :(得分:3)

请参阅java.util.Observable

编辑:Adamski基于PropertyChangeSupport的方法似乎更好观察我建议的方法。

答案 4 :(得分:3)

我相信他们所有人的最简单的解决方案已经错过了一点......

在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,请使用Steve McLeod的解决方案,但根据我的经验,您将希望让您的班级处理它所知道的内容并让其他课程通过Observer接口。

您唯一需要注意的是,您需要在setChanged()之前致电notifyObservers(),否则Observable将不会发送任何事件。

我认为这样你可以调用几个setChanged(),然后只通知观察者一次,让他们知道课程已经改变,并让他们知道如何。

一旦不再需要创建它的类,移除观察者也是一种很好的形式,这样可以防止观察者留在观察者中。只是垃圾收集观察者类不会将其从可观察对象中删除。

如果您想按照每个属性观察课程,我建议您按照上面Adamski所述的PropertyChangeSupport进行操作。如果您希望侦听器能够阻止更改,还可以使用VetoableChangeSupport

答案 5 :(得分:2)

Observer / Observable类从第1天就开始使用Java。不幸的是,最初的设计师在某种程度上搞砸了。公平地说,他们没有机会学习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 {
}

答案 6 :(得分:1)

我在这里的第一个建议是看看AspectJ。这是该语言最适合处理的设计模式之一。以下文章提供了如何实现这一点的非常有说服力的描述:

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

答案 7 :(得分:0)

还有伟大的 Spring 库,提供开箱即用的事件框架

答案 8 :(得分:0)

Guava的EventBus是另一种现成的选择