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

如何在Java中声明和使用事件,java,events,event-handling,Java,Events,Event Handling,我有一个简单的类-将其称为动物。我想在Animal类中触发一个事件,并在实例化Animal类的类中处理它。在事件处理程序中,我想传递一个整数值 我怎样才能完成这样简单的事情呢?参见 编辑:亚当斯基的方法似乎比我建议的更容易观察。我的第一个建议是看看AspectJ。这是语言最擅长处理的设计模式之一。下面的文章非常雄辩地描述了如何实现这一点: 当您希望避免继承类似java.util.Observable的基类时,请使用接口,让您的Observable实现或委托接口的方法 以下是可观察的界面: pub

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

我怎样才能完成这样简单的事情呢?

参见


编辑:亚当斯基的方法似乎比我建议的更容易观察。

我的第一个建议是看看AspectJ。这是语言最擅长处理的设计模式之一。下面的文章非常雄辩地描述了如何实现这一点:


当您希望避免继承类似java.util.Observable的基类时,请使用接口,让您的Observable实现或委托接口的方法

以下是可观察的界面:

public interface IObservable
{
        void addObserver(IObserver o);

        void deleteObserver(IObserver o);

        void notifyObservers(INotification notification);
}
public interface IObserver
{
        void update(INotification notification);
}
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
        }
    }
}
下面是一个助手类,您的真实观察对象可以使用它:

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

}
以下是观察者界面:

public interface IObservable
{
        void addObserver(IObserver o);

        void deleteObserver(IObserver o);

        void notifyObservers(INotification notification);
}
public interface IObserver
{
        void update(INotification notification);
}
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
        }
    }
}
最后,这里是通知接口和基本实现:

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

还有很棒的Spring库,它提供了一个现成的事件框架

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

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 AnimalListener {
    public void animalDoesSomething(int action);
}
notifyObservers(42);
Animal
需要管理其侦听器:

public class Animal {
    private final List<AnimalListener> animalListeners = new ArrayList<AnimalListener>()
    public void addAnimalListener(AnimalListener animalListener) {
        animalListeners.add(animalListener);
    }
}
现在,动物可以触发事件

public class Animal {
    ....
    public void doSomething() {
        for (AnimalListener animalListener : animalListeners) {
            animalListener.animalDoesSomething(4);
        }
    }
}
对于像“触发事件”这样简单的事情,这看起来像是很多代码,但触发事件可能一点也不简单

当然,这个简单机制有各种扩展

  • 我总是让我的事件侦听器扩展
  • 每个侦听器方法的第一个参数应该是事件的源,即
    public void animalDoesSomething(Animal-Animal,int-action)
  • 注册侦听器和事件触发的管理可以抽象为某种抽象事件侦听器管理类。看看你的眼睛,知道我的意思

从第一天起,Observer/Observable类就一直使用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 {
}

假设所传递的整数是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);
    }
  }
}

最后,我建议不要使用
观察者
/
可观察的
,因为这会使代码不可读/难以理解:在向下转换之前,您必须经常使用
instanceof
检查传递给
观察者的参数类型,而且,通过查看特定观察者实现的接口定义,很难看出它所期望的事件类型。定义特定的侦听器实现和事件可以更好地避免这种情况。

番石榴是另一种现成的替代方案

我相信它们中最简单的解决方案都被遗漏了一点

在95%的情况下,您不需要超过此值:

public class Aminal extends Observable {
    public void doSomethingThatNotifiesObservers() {
        setChanged();
        notifyObservers(new Integer(42));
    }
}
我猜你不会自动装箱,所以我制作了一个
Integer
对象,但部分原因是,
Observable
类来自JDK1.0,因此它应该出现在你的Java版本中

使用自动装箱(在Java 1.5及更高版本中)时,
notifyObservers
调用如下所示:

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 AnimalListener {
    public void animalDoesSomething(int action);
}
notifyObservers(42);
为了捕获发送的事件,您需要实现
Observer
接口:

public interface IObservable
{
        void addObserver(IObserver o);

        void deleteObserver(IObserver o);

        void notifyObservers(INotification notification);
}
public interface IObserver
{
        void update(INotification notification);
}
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
类实现了您需要的所有观察者处理

如果您需要能够从外部触发
可观察的
,请使用Steve McLeod的解决方案,但根据我的经验,您希望让您的类处理它知道的内容,并让其他类通过
观察者
接口与之交互

唯一需要注意的是,在
notifyobserver()
Observable
不会发送任何事件之前,需要调用
setChanged()

我认为这是因为您可以调用多个
setChanged()
,然后只通知观察者一次,让他们知道类已更改,并让他们自己决定如何更改

当创建观察者的类不再需要时,移除观察者也是一种很好的形式,这可以防止观察者被留在可观察对象中。仅仅垃圾收集observer类不会将其从observable中移除


如果你想在每个属性的基础上观察这个类,我建议你使用上面Adamski描述的
PropertyChangeSupport
。如果希望侦听器能够阻止更改,还可以使用
VetoableChangeSupport

我的类已经从另一个类继承,我不想在类之间添加一个虚拟对象来绕过单一继承限制。必须有一个更简单的方法。我一直在想可能是这样的。你不是唯一一个:-)看。Andrew提到的基于AspectJ的解决方案是一种选择。其他选项是将可观察模式合并到类Animal本身(它不是那么多代码)中,或者在Animal内部包含和可观察并委托给它。对于一个可重用的解决方案,AspectJ似乎是唯一的选择。这需要很多代码来简单地实现一个事件。我想我要回到C:)。实际上,它做我需要的,只是我需要把它转换成前泛型时代(我在为黑莓写)——它们仍然在java 1.3.很高兴代码能帮助你。