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

Ang*_*ker 10 java events event-handling

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

我该怎么做这样简单的事情?

Ada*_*ski 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);
    }
  }
}
Run Code Online (Sandbox Code Playgroud)

最后,我建议不要使用Observer/Observable因为它使代码难以理解/难以遵循:你经常需要在向下转换之前检查传递给Observer使用的参数的类型instanceof,并且很难看到特定Observer实现的事件类型期待通过查看其接口定义.更好地定义特定的侦听器实现和事件以避免这种情况.


小智 9

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

这是可观察的界面:

public interface IObservable
{
        void addObserver(IObserver o);

        void deleteObserver(IObserver o);

        void notifyObservers(INotification notification);
}
Run Code Online (Sandbox Code Playgroud)

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

}
Run Code Online (Sandbox Code Playgroud)

真正的可观察结果可能如下所示:

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

}
Run Code Online (Sandbox Code Playgroud)

这是观察者界面:

public interface IObserver
{
        void update(INotification notification);
}
Run Code Online (Sandbox Code Playgroud)

最后,这是通知界面和基本实现:

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;
        }
}
Run Code Online (Sandbox Code Playgroud)

  • 哇靠.简单地实现一个事件就是很多代码.我想我会跑回C#:).实际上它做了我需要的,除了我需要将它转换为pre-generics时代(我正在为BlackBerry写作 - 他们仍然在Java 1.3上. (4认同)

Bom*_*mbe 5

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

public interface AnimalListener {
    public void animalDoesSomething(int action);
}
Run Code Online (Sandbox Code Playgroud)

Animal 需要管理其听众:

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

你的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!");
    }
}
Run Code Online (Sandbox Code Playgroud)

现在Animal可以开火了.

public class Animal {
    ....
    public void doSomething() {
        for (AnimalListener animalListener : animalListeners) {
            animalListener.animalDoesSomething(4);
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

对于像"触发事件"这样简单的事情来说,这看起来像很多代码但是可能触发事件并不简单.:)

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

  • 我总是让我的事件监听器扩展java.util.EventListener.
  • 每个侦听器方法的第一个参数应该是事件的来源,即public void animalDoesSomething(Animal animal, int action);.
  • 注册监听器和事件触发的管理可以抽象到某种抽象事件监听器管理类.看看PropertyChangeSupport知道我的意思.