`
turingfellow
  • 浏览: 136448 次
  • 性别: Icon_minigender_1
  • 来自: 福建省莆田市
社区版块
存档分类
最新评论

SimplePropertiesChangeSupport

    博客分类:
  • jade
阅读更多
Listening for bound property changes of a JavaBean is simple enough, and determining which bean fired the event is as straightforward as calling PropertyChangeEvent.getSource(). But what to do if the JavaBean in question has complex property values, each of which can have its own bound properties? This article examines a method for representing targeted events—those that may represent changes in objects other than the source. It also introduces an adapter that allows type-aware property change events using Java 5.0 generics. Examples are provided using the Guise™ Internet application framework.

Typed Property Change Events
When Sun added the JavaBeans component architecture, Java acquired a standard way to conceptualize properties of objects that were richer than mere data fields. Besides the ability to perform custom actions when properties are read and/or written, using so-called getter and setter methods, respectively, Java allowed third-party observer objects to listen for property changes and perform their own actions in response. Such properties which notify listeners of changes are referred to as bound properties. The foundational classes of this architecture are found in the java.beans package.

The two fundamental classes that implement bound properties in JavaBeans are PropertyChangeListener and PropertyChangeEvent. Sun provided the PropertyChangeSupport class to assist developers in creating classes with bound properties, obviating the need for a class to keep track of its own property change listeners and to manually fire property change events to those listeners. Using these classes, a simple Automobile class could implement an engine bound property, assuming that an Engine class exists. (Note that we require that an engine always be non-null to simplify the logic. Without this requirement, the code would need extra checks for null throughout.)

import java.beans.*;

public class Automobile
{
  protected final PropertyChangeSupport propertyChangeSupport;
  private Engine engine;

  /**Default constructor with a default engine.*/
  public Automobile()
  {
    propertyChangeSupport=new PropertyChangeSupport(this);
    engine=new Engine();
  }

  /**@return The car's current engine.*/
  public Engine getEngine() {return engine;}

  /**Sets the car's engine.
  @param newEngine The new engine.
  @exception NullPointerException if the engine is
      <code>null</code>.
  */
  public void setEngine(final Engine newEngine)
  {
    if(newEngine==null)
    {
      throw new NullPointerException("No engine provided.");
    }
    if(!engine.equals(newEngine))
    {
      final Engine oldEngine= engine;
      engine=newEngine;
      propertyChangeSupport.firePropertyChange("engine",
                                               oldEngine,
                                               newEngine);
    }
  }

  /**Add a property change listener for a specific property.
  @param propertyName The name of the property to listen on.
  @param listener The <code>PropertyChangeListener</code>
      to be added.
  */
  public void addPropertyChangeListener(final String propertyName,
      final PropertyChangeListener listener)
  {
    propertyChangeSupport.addPropertyChangeListener(propertyName,
                                                    listener);
  }

  /**Remove a property change listener for a specific property.
  @param propertyName The name of the property that was listened on.
  @param listener The <code>PropertyChangeListener</code>
      to be removed
  */
  public void removePropertyChangeListener(final String propertyName,
      final PropertyChangeListener listener)
  {
    propertyChangeSupport.removePropertyChangeListener(propertyName,
                                                       listener);
  }
}Post a comment
Email Article
Print Article
Share Articles  Digg  del.icio.us  Slashdot  DZone  Reddit  StumbleUpon  Facebook  FriendFeed  Furl  Newsvine  Google  LinkedIn  MySpace  Technorati  Twitter  Windows Live  YahooBuzz An interested class could listen for the car's engine changing by adding a property change listener, usually an anonymous inner class:

final Automobile automobile=new Automobile();
automobile.addPropertyChangeListener("engine",
                                     new PropertyChangeEvent()
    {
      public void propertyChange(
          final PropertyChangeEvent propertyChangeEvent)
      {
        final Engine newEngine=
            (Engine)propertyChangeEvent.getNewValue();
        if(newEngine.getSize()>8)
        {
          Systen.out.println("That's a big engine.");
        }
      }
    });That's easy enough. But the old and new values of PropertyChangeEvent are always expressed as Object, forcing us to cast those values, even when we know ahead of time what type to expect. When we listen for the "engine" property, we expect the value to be an Engine. Wouldn't it be nice if PropertyChangeEvent used generics, so that its values were automatically returned as the expected types?

Unfortunately, PropertyChangeEvent was created long before generics were added to the Java language in version 5.0, but using another Java feature, covariance, it's possible to create an adapter class that provides us with typed values while providing backwards-compatibility with PropertyChangeEvent. This part of the task is surprisingly easy; here is the basic structure of a class, com.garretwilson.beans.GenericPropertyChangeEvent<V>, that does just that:

/**A property value change event is a Java Beans property change
    event retrofitted to use generics to cast to proper value type.
@param <V> The type of property value.
@author Garret Wilson
*/
public class GenericPropertyChangeEvent<V>
    extends PropertyChangeEvent
{

  public GenericPropertyChangeEvent(final Object source,
      final String propertyName, final V oldValue, V newValue)
  {
    super(source, propertyName, oldValue, newValue);
  }

  @SuppressWarnings("unchecked")
  public GenericPropertyChangeEvent(final PropertyChangeEvent
      propertyChangeEvent)
  {
    this(propertyChangeEvent.getSource(),
         propertyChangeEvent.getPropertyName(),
         (V)propertyChangeEvent.getOldValue(),
         (V)propertyChangeEvent.getNewValue());
    setPropagationId(propertyChangeEvent.getPropagationId());
  }

  @SuppressWarnings("unchecked")
  public V getOldValue()
  {
    return (V)super.getOldValue();
  }

  @SuppressWarnings("unchecked")
  public V getNewValue()
  {
    return (V)super.getNewValue();
  }
}So far the main functionality of the class is to cast the old and new values to the generic type, V, before returning them. Because Java doesn't keep track of generics at runtime, the real return types of the getOldValue() and getNewValue() methods after erasure are Object, anyway. The cast to the generic type isn't actually performed here at runtime, so the @SuppressWarnings("unchecked") annotation is needed to prevent the compiler from alerting us to this fact. Whatever code uses these methods will perform the appropriate cast, however, providing equivalence to the property change listener code we had earlier, except without the need to code casts by hand. We've even provided a copy constructor to allow creating generic property change events from standard non-generic property change events.

There's a problem, however: the Automobile class fires a normal PropertyChangeEvent, not a GenericPropertyChangeEvent<V>. We could change the Automobile class to throw a GenericPropertyChangeEvent<V>, which is compatible with PropertyChangeEvent, but then we'd have to cast the event inside our PropertyChangeListener, which defeats the purpose of this exercise. It looks like we'll have to create a custom GenericPropertyChangeListener<V> as well:

/**A Java Beans property change listener retrofitted
    to use generics to cast to proper value type.
@param <V> The type of property value.
@author Garret Wilson
*/
public interface GenericPropertyChangeListener<V>
    extends PropertyChangeListener
{
  public void propertyChange(final GenericPropertyChangeEvent<V>
      genericPropertyChangeEvent);
}Well, that was certainly easy enough. But more problems arise. First, the java.beans.PropertyChangeSupport class keeps track of PropertyChangeListeners, not GenericPropertyChangeListeners, and therefore will call the PropertyChangeListener.propertyChange() method rather than the generic version. We could scrap PropertyChangeSupport and keep track of the listeners ourselves, but there bigger problem: our Automobile class would no longer be compatible with the standard Java bound property contract, which requires registration of PropertyChangeListeners and firing of property change events to the non-generics-aware version of propertyChange(). Third party tools would not be able to work with our JavaBean.

The trick is to use a special base listener class that is compatible with both PropertyChangeListener and GenericPropertyChangeListener<V> and that converts the PropertyChangeEvent to a GenericPropertyChangeEvent<V> as needed. That class, com.garretwilson.beans.AbstractGenericPropertyChangeListener, is presented below:

/**A Java Beans property change listener
    retrofitted to use generics to cast to proper value type.
@param <V> The type of property value.
@author Garret Wilson
*/
public abstract class AbstractGenericPropertyChangeListener<V>
   implements GenericPropertyChangeListener<V>
{

  /**Called when a bound property is changed.
  This non-generics version calls the generic version,
      creating a new event if necessary.
  No checks are made at compile time to ensure the given event
      actually supports the given generic type.
  @param propertyChangeEvent An event object describing
      the event source, the property that has changed,
      and its old and new values.
  @see GenericPropertyChangeListener#propertyChange
      (GenericPropertyChangeEvent)
  */
  @SuppressWarnings("unchecked")
  public final void propertyChange(final PropertyChangeEvent
      propertyChangeEvent)
  {
    propertyChange((GenericPropertyChangeEvent<V>)
        getGenericPropertyChangeEvent(propertyChangeEvent));
  }

  /**Converts a property change event to a generics-aware
      property value change event.
  @param propertyChangeEvent An event object describing
      the event source, the property that has changed,
      and its old and new values.
  @return A generics-aware property change event,
      either cast from the provided object
      or created from the provided object's values as appropriate.
  */
  @SuppressWarnings("unchecked")
  public static <T> GenericPropertyChangeEvent<T>
      getGenericPropertyChangeEvent(final PropertyChangeEvent
      propertyChangeEvent)
  {
    if(propertyChangeEvent instanceof GenericPropertyChangeEvent)
    {
      return (GenericPropertyChangeEvent<T>)propertyChangeEvent;
    }
    else  //if the event is a normal property change event
    {
      return new GenericPropertyChangeEvent<T>(
          propertyChangeEvent);  //create a copy of the event
    }
  }
}Fulfilling the contract of PropertyChangeListener, this class overrides propertyChange(PropertyChangeEvent), but then converts the event to a GenericPropertyChangeEvent<T> and passes it to the generic method version, propertyChange(GenericPropertyChangeEvent<T>). The conversion in the utility method <T> GenericPropertyChangeEvent<T> getGenericPropertyChangeEvent(final PropertyChangeEvent propertyChangeEvent) is efficient: if the event is already a GenericPropertyChangeEvent<T>, there's no need to create a new event. Otherwise, the method creates a new GenericPropertyChangeEvent<T> from the plain PropertyChangeEvent using the copy constructor we created above.

To use this efficiency, we'll need to modify the Automobile class to fire GenericPropertyChangeEvents. (The class would work just fine without this, but would require the unnecessary creation of new objects from generics-aware listeners.) It turns out that PropertyChangeSupport.firePropertyChange(String, Object, Object) just creates a PropertyChangeEvent and sends that to PropertyChangeSupport.firePropertyChange(PropertyChangeEvent). The Automobile class can create its own GenericPropertyChangeEvent<Engine> and fire that object when needed:

/**Sets the car's engine.
@param newEngine The new engine.
@exception NullPointerException if the engine
    is <code>null</code>.
*/
public void setEngine(final Engine newEngine)
{
  if(newEngine==null)
  {
    throw new NullPointerException("No engine provided.");
  }
  if(!engine.equals(newEngine))
  {
    final Engine oldEngine= engine;
    engine=newEngine;
    final PropertyChangeEvent propertyChangeEvent=
        new GenericPropertyChangeEvent<Engine>(this,
                                                     "engine",
                                                     oldEngine,
                                                     newEngine);
    propertyChangeSupport.firePropertyChange(propertyChangeEvent);
  }
}All the pieces are now in place. We can now listen to the car's engine changing using our generics-aware classes with no casts:

final Automobile automobile=new Automobile();
automobile.addPropertyChangeListener("engine",
    new AbstractGenericPropertyChangeEvent<Engine>()
    {
      public void propertyChange(
          final GenericPropertyChangeEvent<Engine>
          propertyChangeEvent)
      {
        final Engine newEngine=propertyChangeEvent.getNewValue();
        if(newEngine.getSize()>8)
        {
          Systen.out.println("That's a big engine.");
        }
      }
    });Furthermore, non-generics-aware PropertyChangeListener instances can still register with an Automobile instance, and they will function as normal. Even better, we can use the AbstractGenericPropertyChangeEvent<V> registration pattern to register with any JavaBean, whether or not it is generics-aware. Our code will perform casts implicitly, obviating the need for us to perform this tedious and error-prone process ourselves. This pattern additionally imposes consistency on our code—if we copy part of this routine to another property change listener that expects a type other than Engine, our code won't compile, whereas an erroneous hand-coded cast to Engine in a non-generics-aware property change listener would have gone unnoticed until runtime.

Targeted Property Change Events
Our current scheme works well for changing automobile engines, and for objects with simple properties the standard JavaBeans bound property paradigm gets the job done. But for complex properties—those properties the values of which have their own properties that can change—this paradigm has it limitations. What if some outside object want to listen for changes in the engine size as well, for example?

One solution would be for the external object to register a property change listener directly with the Automobile's engine property value. This quickly becomes cumbersome, because the value of the engine property can change. The external class must therefore also register a property change listener with the Automobile instance, listening for the "engine" property to change, and then unregister its event listener from the old engine and register it with the new one. Remembering to do this (not to mention documenting that this must be done) becomes unmanageable. Wouldn't it be better if we could do this once in the Automobile class, and propagate any property changes in the Engine to the property change listeners of Automobile?

This is a valid approach, but it leaves one detail unresolved: How does a PropertyChangeListener determine whether a property change was performed on the Automobile or on its contained Engine? We could have the Automobile forward the Engine property change events unchanged, so that PropertyChangeEvent.getSource() would equal the Engine instance as appropriate, but if one property change listener is listening to several Automobiles it prevents them from using PropertyChangeEvent.getSource() to find which Automobile's Engine changed properties. Besides, the contract of PropertyChangeEvent.getSource() (as stated by the API of java.util.EventObject) is that "All Events are constructed with a reference to the object, the "source", that is logically deemed to be the object upon which the Event in question initially occurred upon." While this is slightly ambiguous in our case as to whether "initially occurred upon" refers to the object that changed its properties or the object that fired the event, it seems pragmatically useful for a PropertyChangeListener to expect PropertyChangeEvent.getSource() to refer to the object with which the listener registered. This would favor the "object that fired the event as source" interpretation.

So there needs to be a separate method of PropertyChangeEvent that indicates the object the property of which changed, regardless of which object is firing the event. This value would stay the same regardless of how many times the event was propagated. Indeed it is possible the authors of Java had something similar in mind for the future, for the API of java.beans.PropertyChangeEvent.getPropagationId() states:

Post a comment
Email Article
Print Article
Share Articles  Digg  del.icio.us  Slashdot  DZone  Reddit  StumbleUpon  Facebook  FriendFeed  Furl  Newsvine  Google  LinkedIn  MySpace  Technorati  Twitter  Windows Live  YahooBuzz The "propagationId" field is reserved for future use. In Beans 1.0 the sole requirement is that if a listener catches a PropertyChangeEvent and then fires a PropertyChangeEvent of its own, then it should make sure that it propagates the propagationId field from its incoming event to its outgoing event.

That sounds a lot like the functionality we want, but with no more information on semantics, and with no conventions relating to this field in widespread use, it seems foolhardy to hijack this field for own purposes. If future versions of Java were to use the propagationId field for some other purpose, it could cause serious backwards-compatibility issues with our code. It again seems that the safest route is to add in our own extensions to the JavaBeans component architecture.

We can start by creating an interface that can be mixed in with any event and that indicates the target of the event—the object to which the action was directed that caused the event, whether or not that object actually fired the event object in question. This is done in com.garretwilson.event.TargetedEvent, which could be used with any type of event, such as a property change event or an action event:

/**An interface for an event that knows its target,
    or the object to which the event applies
    (which may be different than the object that fired the event).
@author Garret Wilson
*/
public interface TargetedEvent
{

  /**Returns the object to which the event applies.
  This may be a different than <dfn>source</dfn>,
      which is the object that generated this event instance.
  @return The target of the event, or <code>null</code>
      if the event target is not known.
  */
  public Object getTarget();
}We can now make GenericPropertyChangeEvent<V>, which we created earlier, compatible with our new targeted event framework. We'll add new copy constructors that will allow new events to be created with different sources while still maintaining the same target. We'll also upgrade the old source-only constructors to automatically set the target to the same value as the source, for those instances in which there is no need to maintain a separate target and the class is being used traditionally:

/**A property value change event is a Java Beans
    property change event retrofitted to use generics
    to cast to proper value type.
This event is also <dfn>targeted</dfn>,
    specifying an event target which may or may not
    be the same as the source object firing this event.
@param <V> The type of property value.
@author Garret Wilson
*/
public class GenericPropertyChangeEvent<V>
    extends PropertyChangeEvent implements TargetedEvent
{

  /**The target of the event, or <code>null</code>
      if the event target is not known.*/
  private final Object target;

    /**Returns the object to which the event applies.
    This may be a different than <dfn>source</dfn>,
        which is the object that generated this event instance.
    @return The target of the event.
    */
    public Object getTarget() {return target;}

  /**Source  and property name constructor with old and new values.
  The target will be set to be the same as the given source.
  @param source The bean that fired the event.
  @param propertyName The programmatic name of the property
      that was changed.
  @param oldValue The old value of the property,
      or <code>null</code> if no old value is not available.
  @param newValue The new value of the property,
      or <code>null</code> if the new value is not available.
  */
  public GenericPropertyChangeEvent(final Object source,
      final String propertyName, final V oldValue, V newValue)
  {
    this(source, source, propertyName, oldValue, newValue);
  }

  /**Source, target, and property name constructor
      with old and new values.
  @param source The bean that fired the event.
  @param target The target of the event.
  @param propertyName The programmatic name of the property
      that was changed.
  @param oldValue The old value of the property,
      or <code>null</code> if no old value is not available.
  @param newValue The new value of the property,
      or <code>null</code> if the new value is not available.
  */
  public GenericPropertyChangeEvent(final Object source,
      final Object target, final String propertyName,
      final V oldValue, V newValue)
  {
    super(source, propertyName, oldValue, newValue);
    this.target= target;
  }

  /**Property change event copy constructor.
  @param propertyChangeEvent A property change event the values
      of which will later cast to this class' generic type.
  */
  public GenericPropertyChangeEvent(final PropertyChangeEvent
      propertyChangeEvent)
  {
    this(propertyChangeEvent.getSource(), propertyChangeEvent);
  }

  /**Property change event copy constructor
      that specifies a different source.
  If the property change event is a {@link TargetedEvent},
      the target is copied from that event;
      otherwise, the given source will be used as the target.
  @param source The object on which the event initially occurred.
  @param propertyChangeEvent A property change event the values
      of which will later cast to this class' generic type.
  */
  @SuppressWarnings("unchecked")
  public GenericPropertyChangeEvent(final Object source,
      final PropertyChangeEvent propertyChangeEvent)
  {
    this(source, propertyChangeEvent instanceof TargetedEvent
        ? ((TargetedEvent)propertyChangeEvent).getTarget() : source,
        propertyChangeEvent.getPropertyName(),
        (V)propertyChangeEvent.getOldValue(),
        (V)propertyChangeEvent.getNewValue());
    setPropagationId(propertyChangeEvent.getPropagationId());
  }

  /**@return The old value of the property,
      or <code>null</code> if the old value is not available.*/
  @SuppressWarnings("unchecked")
  public V getOldValue()
  {
    return (V)super.getOldValue();
  }

  /**@return The new value of the property,
      or <code>null</code> if the new value is not available.*/
  @SuppressWarnings("unchecked")
  public V getNewValue()
  {
    return (V)super.getNewValue();
  }
}The Automobile class can now repeat any property changes of the contained Engine object by creating a new event object with a new source, but keeping the same target. This is best done using the GenericPropertyChangeEvent<V> copy constructor above that takes a different source as one of its arguments. The new Automobile class is shown below:

public class Automobile
{
  protected final PropertyChangeSupport propertyChangeSupport;
  protected final PropertyChangeListener repeatPropertyChangeListener=
      new PropertyChangeListener()
      {
        public void propertyChange(final PropertyChangeEvent
            propertyChangeEvent)
            {
              final PropertyChangeEvent repeatPropertyChangeEvent=
                  new GenericPropertyChangeEvent<Object>(
                  Automobile.this, propertyChangeEvent);
              propertyChangeSupport.firePropertyChange(
                  repeatPropertyChangeEvent);
            }
      };
  private Engine engine;

  /**Default constructor with a default engine.*/
  public Automobile()
  {
    propertyChangeSupport=new PropertyChangeSupport(this);
    engine=new Engine();
    engine.addPropertyChangeListener(repeatPropertyChangeListener);
    addPropertyChangeListener("engine",
        new AbstractGenericPropertyChangeEvent<Engine>()
        {
          public void propertyChange(
              final GenericPropertyChangeEvent<Engine>
              propertyChangeEvent)
          {
            propertyChangeEvent.getOldValue().
                removePropertyChangeListener(repeatPropertyChangeListener);
            propertyChangeEvent.getNewValue().
                addPropertyChangeListener(repeatPropertyChangeListener);
          }
        });
  }

  /**@return The car's current engine.*/
  public Engine getEngine() {return engine;}

  /**Sets the car's engine.
  @param newEngine The new engine.
  @exception NullPointerException if the engine
      is <code>null</code>.
  */
  public void setEngine(final Engine newEngine)
  {
    if(newEngine==null)
    {
      throw new NullPointerException("No engine provided.");
    }
    if(!engine.equals(newEngine))
    {
      final Engine oldEngine= engine;
      engine=newEngine;
      final PropertyChangeEvent propertyChangeEvent=
          new GenericPropertyChangeEvent<Engine>(this,
                                                       "engine",
                                                       oldEngine,
                                                       newEngine);
      propertyChangeSupport.firePropertyChange(propertyChangeEvent);
    }
  }

  /**Add a property change listener for a specific property.
  @param propertyName The name of the property to listen on.
  @param listener The <code>PropertyChangeListener</code>
      to be added.
  */
  public void addPropertyChangeListener(final String propertyName,
      final PropertyChangeListener listener)
  {
    propertyChangeSupport.addPropertyChangeListener(propertyName,
                                                    listener);
  }

  /**Remove a property change listener for a specific property.
  @param propertyName The name of the property that was listened on.
  @param listener The <code>PropertyChangeListener</code>
      to be removed
  */
  public void removePropertyChangeListener(final String propertyName,
      final PropertyChangeListener listener)
  {
    propertyChangeSupport.removePropertyChangeListener(propertyName,
                                                       listener);
  }
}There are three important parts to this property change repeater pattern. The first is the repeater PropertyChangeListener, which can be registered with any class. It takes whatever property change that has occurred and refires the event after creating a new event with an updated source of the object firing the event. (It is important to note that Automobile.this is used to represent the Automobile instance as the source rather than the anonymous inner class.) Secondly, the repeater is registered with the Engine instance as soon as it is created. Thirdly, because the engine property can change, a separate listener is registered with the Automobile instance itself for notification of when the engine property value changes. When this happens, the repeater is unregistered from the old Engine and then registered with the new Engine.

Now an external class can be notified of property changes of the Engine property of the Automobile by registering with the Automobile either for all property change events or only for specific Engine-specific properties. If there is ever a question of whether a property of Automobile or Engine has changed, this may be resolved by examining TargetedEvent.getTarget() if the PropertyChangeEvent in question implements TargetedEvent.

The concept of a targeted event is especially useful when an object can have an arbitrary number of contained objects over an arbitrary number of hierarchical levels. As an example, consider the TreeControl component in the com.guiseframework.component package of the Guise™ Internet application framework. A TreeControl contains a root TreeNodeModel<V> which itself can contain an arbitrary number of TreeNodeModel<V> children, each of which in turn can contain other tree nodes, and so on. A common use case is to listen for the selection by the user of one of the nodes in a tree.

An application simply cannot practically register and unregister a property change listeners with each TreeNodeModel<V> as it is added or removed from the tree. The Guise™ TreeControl therefore uses the typed, targeted property change architecture described above to propagate property changes of each TreeNodeModel<V> up the tree hierarchy until it is finally repeated from the TreeControl. Any property change event fired from the control will properly indicate the TreeControl as its source, and indicate the tree node with the changed property as its target. An application can therefore listen directly to the TreeControl to be notified when a tree node is selected, for example:

final TreeControl treeControl=new TreeControl();
treeControl.addPropertyChangeListener(
    TreeNodeModel.SELECTED_PROPERTY,
    new AbstractGenericPropertyChangeListener<Boolean>()
    {
      public void propertyChange(
          final GenericPropertyChangeEvent<Boolean>
          propertyChangeEvent)
      {
        if(propertyChangeEvent.getTarget() instanceof TreeNodeModel)
        {
          final TreeNodeModel<?> treeNodeModel=
              (TreeNodeModel<?>)propertyChangeEvent.getTarget();
          if(Boolean.TRUE.equals(propertyChangeEvent.getNewValue()))
          {
            //the tree node was just selected
          }
        }
      }
    });The application listens for the TreeNodeModel.SELECTED_PROPERTY changing by registering one property change listener with the TreeControl instance. Once the property changes, the listener verifies that a tree node is indeed the object with the changed selection status. If the new selected status is true, the application may decide to perform some special action resulting from the selection change, perhaps based upon the specific tree node target as reported in propertyChangeEvent.getTarget().

Summary
This JavaBean extension architecture of typed, targeted property change events not only is simple and powerful, it also is interoperable with the existing JavaBeans component architecture. Through the use of generics it obviates the need for tedious manual casting of values while imposing consistency and internal type checking within a property change listener. By introducing the concept of an event target, the framework allows facile propagation of property change events to a single listening point without losing the identification of the original object the property of which was changed. A developer can experiment with the examples presented here using the free development version of the Guise™ framework library, available at http://www.guiseframework.com/.

分享到:
评论

相关推荐

    MATLAB实现基于LSTM-AdaBoost长短期记忆网络结合AdaBoost时间序列预测(含模型描述及示例代码)

    内容概要:本文档详细介绍了基于 MATLAB 实现的 LSTM-AdaBoost 时间序列预测模型,涵盖项目背景、目标、挑战、特点、应用领域以及模型架构和代码示例。随着大数据和AI的发展,时间序列预测变得至关重要。传统方法如 ARIMA 在复杂非线性序列中表现欠佳,因此引入了 LSTM 来捕捉长期依赖性。但 LSTM 存在易陷局部最优、对噪声鲁棒性差的问题,故加入 AdaBoost 提高模型准确性和鲁棒性。两者结合能更好应对非线性和长期依赖的数据,提供更稳定的预测。项目还展示了如何在 MATLAB 中具体实现模型的各个环节。 适用人群:对时间序列预测感兴趣的开发者、研究人员及学生,特别是有一定 MATLAB 编程经验和熟悉深度学习或机器学习基础知识的人群。 使用场景及目标:①适用于金融市场价格预测、气象预报、工业生产故障检测等多种需要时间序列分析的场合;②帮助使用者理解并掌握将LSTM与AdaBoost结合的实现细节及其在提高预测精度和抗噪方面的优势。 其他说明:尽管该模型有诸多优点,但仍存在训练时间长、计算成本高等挑战。文中提及通过优化数据预处理、调整超参数等方式改进性能。同时给出了完整的MATLAB代码实现,便于学习与复现。

    palkert_3ck_01_0918.pdf

    palkert_3ck_01_0918

    pepeljugoski_01_1106.pdf

    pepeljugoski_01_1106

    tatah_01_1107.pdf

    tatah_01_1107

    [AB PLC例程源码][MMS_046393]Motor Speed Reference.zip

    AB PLC例程代码项目案例 【备注】 1、该资源内项目代码都经过测试运行成功,功能ok的情况下才上传的,请放心下载使用!有问题请及时沟通交流。 2、适用人群:计算机相关专业(如计科、信息安全、数据科学与大数据技术、人工智能、通信、物联网、自动化、电子信息等)在校学生、专业老师或者企业员工下载使用。 3、用途:项目具有较高的学习借鉴价值,不仅适用于小白学习入门进阶。也可作为毕设项目、课程设计、大作业、初期项目立项演示等。 4、如果基础还行,或热爱钻研,亦可在此项目代码基础上进行修改添加,实现其他不同功能。 欢迎下载!欢迎交流学习!不清楚的可以私信问我!

    基于51的步进电机控制系统20250302

    题目:基于单片机的步进电机控制系统 模块: 主控:AT89C52RC 步进电机(ULN2003驱动) 按键(3个) 蓝牙(虚拟终端模拟) 功能: 1、可以通过蓝牙远程控制步进电机转动 2、可以通过按键实现手动与自动控制模式切换。 3、自动模式下,步进电机正转一圈,反转一圈,循环 4、手动模式下可以通过按键控制步进电机转动(顺时针和逆时针)

    [AB PLC例程源码][MMS_041234]Logix Fault Handler.zip

    AB PLC例程代码项目案例 【备注】 1、该资源内项目代码都经过测试运行成功,功能ok的情况下才上传的,请放心下载使用!有问题请及时沟通交流。 2、适用人群:计算机相关专业(如计科、信息安全、数据科学与大数据技术、人工智能、通信、物联网、自动化、电子信息等)在校学生、专业老师或者企业员工下载使用。 3、用途:项目具有较高的学习借鉴价值,不仅适用于小白学习入门进阶。也可作为毕设项目、课程设计、大作业、初期项目立项演示等。 4、如果基础还行,或热爱钻研,亦可在此项目代码基础上进行修改添加,实现其他不同功能。 欢迎下载!欢迎交流学习!不清楚的可以私信问我!

    [AB PLC例程源码][MMS_042348]Using an Ultra3000 as an Indexer on DeviceNet with a CompactLogix.zip

    AB PLC例程代码项目案例 【备注】 1、该资源内项目代码都经过测试运行成功,功能ok的情况下才上传的,请放心下载使用!有问题请及时沟通交流。 2、适用人群:计算机相关专业(如计科、信息安全、数据科学与大数据技术、人工智能、通信、物联网、自动化、电子信息等)在校学生、专业老师或者企业员工下载使用。 3、用途:项目具有较高的学习借鉴价值,不仅适用于小白学习入门进阶。也可作为毕设项目、课程设计、大作业、初期项目立项演示等。 4、如果基础还行,或热爱钻研,亦可在此项目代码基础上进行修改添加,实现其他不同功能。 欢迎下载!欢迎交流学习!不清楚的可以私信问我!

    智慧校园平台建设全流程详解:从需求到持续优化

    内容概要:本文详细介绍了建设智慧校园平台所需的六个关键步骤。首先通过需求分析深入了解并确定校方和使用者的具体需求;其次是规划设计阶段,依据所得需求制定全面的建设方案。再者是对现有系统的整合——系统集成,确保新旧平台之间的互操作性和数据一致性。培训支持帮助全校教职工和学生快速熟悉新平台,提高效率。实施试点确保系统逐步稳定部署。最后,强调持续改进的重要性,以适应技术和环境变化。通过这一系列有序的工作,可以使智慧校园建设更为科学高效,减少失败风险。 适用人群:教育领域的决策者和技术人员,包括负责信息化建设和运维的团队成员。 使用场景及目标:用于指导高校和其他各级各类学校规划和发展自身的数字校园生态链;目的是建立更加便捷高效的现代化管理模式和服务机制。 其他说明:智慧校园不仅仅是简单的IT设施升级或软件安装,它涉及到全校范围内的流程再造和创新改革。

    AI淘金实战手册:100+高收益变现案例解析

    该文档系统梳理了人工智能技术在商业场景中的落地路径,聚焦内容生产、电商运营、智能客服、数据分析等12个高潜力领域,提炼出100个可操作性变现模型。内容涵盖AI工具开发、API服务收费、垂直场景解决方案、数据增值服务等多元商业模式,每个思路均配备应用场景拆解、技术实现路径及收益测算框架。重点呈现低代码工具应用、现有平台流量复用、细分领域自动化改造三类轻量化启动方案,为创业者提供从技术选型到盈利闭环的全流程参考。

    palkert_3ck_02_0719.pdf

    palkert_3ck_02_0719

    2006-2023年 地级市-克鲁格曼专业化指数.zip

    克鲁格曼专业化指数,最初是由Krugman于1991年提出,用于反映地区间产业结构的差异,也被用来衡量两个地区间的专业化水平,因而又称地区间专业化指数。该指数的计算公式及其含义可以因应用背景和具体需求的不同而有所调整,但核心都是衡量地区间的产业结构差异或专业化程度。 指标 年份、城市、第一产业人数(first_industry1)、第二产业人数(second_industry1)、第三产业人数(third_industry1)、专业化指数(ksi)。

    [AB PLC例程源码][MMS_046305]R2FX.zip

    AB PLC例程代码项目案例 【备注】 1、该资源内项目代码都经过测试运行成功,功能ok的情况下才上传的,请放心下载使用!有问题请及时沟通交流。 2、适用人群:计算机相关专业(如计科、信息安全、数据科学与大数据技术、人工智能、通信、物联网、自动化、电子信息等)在校学生、专业老师或者企业员工下载使用。 3、用途:项目具有较高的学习借鉴价值,不仅适用于小白学习入门进阶。也可作为毕设项目、课程设计、大作业、初期项目立项演示等。 4、如果基础还行,或热爱钻研,亦可在此项目代码基础上进行修改添加,实现其他不同功能。 欢迎下载!欢迎交流学习!不清楚的可以私信问我!

    精品推荐-通信技术LTE干货资料合集(19份).zip

    精品推荐,通信技术LTE干货资料合集,19份。 LTE PCI网络规划工具.xlsx LTE-S1切换占比专题优化分析报告.docx LTE_TDD问题定位指导书-吞吐量篇.docx LTE三大常见指标优化指导书.xlsx LTE互操作邻区配置核查原则.docx LTE信令流程详解指导书.docx LTE切换问题定位指导一(定位思路和问题现象).docx LTE劣化小区优化指导手册.docx LTE容量优化高负荷小区优化指导书.docx LTE小区搜索过程学习.docx LTE小区级与邻区级切换参数说明.docx LTE差小区处理思路和步骤.docx LTE干扰日常分析介绍.docx LTE异频同频切换.docx LTE弱覆盖问题分析与优化.docx LTE网优电话面试问题-应答技巧.docx LTE网络切换优化.docx LTE高负荷小区容量优化指导书.docx LTE高铁优化之多频组网优化提升“用户感知,网络价值”.docx

    matlab程序代码项目案例:matlab程序代码项目案例matlab中Toolbox中带有的模型预测工具箱.zip

    matlab程序代码项目案例 【备注】 1、该资源内项目代码都经过测试运行成功,功能ok的情况下才上传的,请放心下载使用!有问题请及时沟通交流。 2、适用人群:计算机相关专业(如计科、信息安全、数据科学与大数据技术、人工智能、通信、物联网、自动化、电子信息等)在校学生、专业老师或者企业员工下载使用。 3、用途:项目具有较高的学习借鉴价值,不仅适用于小白学习入门进阶。也可作为毕设项目、课程设计、大作业、初期项目立项演示等。 4、如果基础还行,或热爱钻研,亦可在此项目代码基础上进行修改添加,实现其他不同功能。 欢迎下载!欢迎交流学习!不清楚的可以私信问我!

    pepeljugoski_01_0508.pdf

    pepeljugoski_01_0508

    szczepanek_01_0308.pdf

    szczepanek_01_0308

    oif2007.384.01_IEEE.pdf

    oif2007.384.01_IEEE

    stone_3ck_01_0119.pdf

    stone_3ck_01_0119

    oganessyan_01_1107.pdf

    oganessyan_01_1107

Global site tag (gtag.js) - Google Analytics