- 浏览: 135223 次
- 性别:
- 来自: 福建省莆田市
文章分类
最新评论
-
houruiming:
tks for your info which helps m ...
setcontent和setcontentobject用的是同一片内存 -
turingfellow:
in.tftpd -l -s /home/tmp -u ro ...
commands -
turingfellow:
LINUX下的网络设置 ifconfig ,routeLINU ...
commands -
turingfellow:
安装 linux loopbackyum install um ...
commands
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/.
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/.
发表评论
-
protocols
2011-04-03 19:22 925<!-- The protocols capabilit ... -
dfcap
2011-04-03 19:15 876<!-- The df capability has a ... -
booktrading /seller
2011-03-29 23:19 928<html><head><tit ... -
booktrading / manager
2011-03-29 23:18 1093<html><head><tit ... -
booktrading / common
2011-03-29 23:17 987<html><head><tit ... -
booktrading / buyer
2011-03-29 23:13 846<!-- <H3>The buyer age ... -
tomcat的context说明书
2011-03-20 17:39 803http://tomcat.apache.org/tomcat ... -
msyql的select语法
2010-09-13 22:52 107613.2.7. SELECT语法 13.2.7.1. ... -
zotero与word集成
2010-09-11 08:50 1768Manually Installing the Zotero ... -
university 2/n
2010-08-24 07:54 898Chapter 1.Introduction of regis ... -
university 1/n
2010-08-24 07:53 940chapter? Introduction ?.?The st ... -
Sun Java Bugs that affect lucene
2010-08-23 08:59 735Sometimes Lucene runs amok of b ... -
Snowball分词
2010-08-22 13:07 1226using System; using Lucene.Net. ... -
penn tree bank 6/6
2010-08-20 07:09 91811 This use of 12 Contact the - ... -
penn tree bank 5/n
2010-08-19 07:40 923always errs on the side of caut ... -
penn tree bank 4/n
2010-08-19 07:39 8184. Bracketing 4.1 Basic Methodo ... -
penn tree bank 3/n
2010-08-15 23:31 8192.3.1 Automated Stage. During t ... -
penn tree bank 2/n
2010-08-15 23:30 1505Mitchell P Marcus et al. Buildi ... -
capabilities 3/3
2010-08-11 22:58 77901<capability xmlns="ht ... -
capabilities 2/3
2010-08-11 22:57 740Fig.3.Element creation cases:a) ...
相关推荐
【项目资源】: 包含前端、后端、移动开发、操作系统、人工智能、物联网、信息化管理、数据库、硬件开发、大数据、课程资源、音视频、网站开发等各种技术项目的源码。 包括STM32、ESP8266、PHP、QT、Linux、iOS、C++、Java、python、web、C#、EDA、proteus、RTOS等项目的源码。 【项目质量】: 所有源码都经过严格测试,可以直接运行。 功能在确认正常工作后才上传。 【适用人群】: 适用于希望学习不同技术领域的小白或进阶学习者。 可作为毕设项目、课程设计、大作业、工程实训或初期项目立项。 【附加价值】: 项目具有较高的学习借鉴价值,也可直接拿来修改复刻。 对于有一定基础或热衷于研究的人来说,可以在这些基础代码上进行修改和扩展,实现其他功能。 【沟通交流】: 有任何使用上的问题,欢迎随时与博主沟通,博主会及时解答。 鼓励下载和使用,并欢迎大家互相学习,共同进步。。内容来源于网络分享,如有侵权请联系我删除。另外如果没有积分的同学需要下载,请私信我。
精选毕设项目-宅男社区
精选毕设项目-扫描条形码
配网两阶段鲁棒优化调度模型 关键词:两阶段鲁棒优化,CCG算法,储能 仿真算例采用33节点,采用matlab+yalmip+cplex编写,两阶段模型采用CCG算法求解。 模型中一阶段变量主要包括01变量和无功优化变量,核心变量主要存在于二阶段,因此在叠加二阶段变量优化过程中更容易得到最优解,所以有限次迭代即得到收敛的结果。 模型以网损为目标,包括功率平衡、网络潮流、电压电流、蓄电池出力以及无功设备出力等约束。 复现《两阶段鲁棒优化的主动配电网动态无功优化》-熊壮壮,具体内容可自行下载了解。
comsol光栅仿真 计算复合波导光栅准BIC增强古斯汉森位移
精选毕设项目-车源宝寻车广场
数字农业产业项目整体解决方案
精选毕设项目-幸运大抽奖
SRS构型七自由度冗余机械臂运动学建模全套matlab代码 代码主要功能: [1]. 基于臂角参数化方法求解机械臂在给定末端位姿和臂角下的关节角度; [2]. 求解机械臂在给定末端位姿下的有效臂角范围,有效即在该区间内机械臂关节角度不会超出关节限位; [3]. 以避关节限位为目标在有效臂角区间内进行最优臂角的选取,进而获取机械臂在给定末端位姿下的最优关节角度。 购前须知: 1. 代码均为个人手写,主要包含运动学建模全套代码; 2. 代码已经包含必要的注释; 包含原理推导文档,不包含绘图脚本以及urdf;
精选毕设项目-微信小程序天气源码
bmjebm-29-6.pdf
chromedriver-linux64_123.0.6273.0
精选毕设项目-腾讯云小程序一站式解决方案
精选毕设项目-仿饿了么
Spring Boot是Spring框架的一个模块,它简化了基于Spring应用程序的创建和部署过程。Spring Boot提供了快速启动Spring应用程序的能力,通过自动配置、微服务支持和独立运行的特性,使得开发者能够专注于业务逻辑,而不是配置细节。Spring Boot的核心思想是约定优于配置,它通过自动配置机制,根据项目中添加的依赖自动配置Spring应用。这大大减少了配置文件的编写,提高了开发效率。Spring Boot还支持嵌入式服务器,如Tomcat、Jetty和Undertow,使得开发者无需部署WAR文件到外部服务器即可运行Spring应用。 Java是一种广泛使用的高级编程语言,由Sun Microsystems公司(现为Oracle公司的一部分)在1995年首次发布。Java以其“编写一次,到处运行”(WORA)的特性而闻名,这一特性得益于Java虚拟机(JVM)的使用,它允许Java程序在任何安装了相应JVM的平台上运行,而无需重新编译。Java语言设计之初就是为了跨平台,同时具备面向对象、并发、安全和健壮性等特点。 Java语言广泛应用于企业级应用、移动应用、桌面应用、游戏开发、云计算和物联网等领域。它的语法结构清晰,易于学习和使用,同时提供了丰富的API库,支持多种编程范式,包括面向对象、命令式、函数式和并发编程。Java的强类型系统和自动内存管理减少了程序错误和内存泄漏的风险。随着Java的不断更新和发展,它已经成为一个成熟的生态系统,拥有庞大的开发者社区和持续的技术创新。Java 8引入了Lambda表达式,进一步简化了并发编程和函数式编程的实现。Java 9及以后的版本继续在模块化、性能和安全性方面进行改进,确保Java语言能够适应不断变化的技术需求和市场趋势。 MySQL是一个关系型数据库管理系统(RDBMS),它基于结构化查询语言(SQL)来管理和存储数据。MySQL由瑞典MySQL AB公司开发,并于2008年被Sun Microsystems收购,随后在2010年,Oracle公司收购了Sun Microsystems,从而获得了MySQL的所有权。MySQL以其高性能、可靠性和易用性而闻名,它提供了多种特性来满足不同规模应用程序的需求。作为一个开源解决方案,MySQL拥有一个活跃的社区,不断为其发展和改进做出贡献。它的多线程功能允许同时处理多个查询,而其优化器则可以高效地执行复杂的查询操作。 随着互联网和Web应用的快速发展,MySQL已成为许多开发者和公司的首选数据库之一。它的可扩展性和灵活性使其能够处理从小规模应用到大规模企业级应用的各种需求。通过各种存储引擎,MySQL能够适应不同的数据存储和检索需求,从而为用户提供了高度的定制性和性能优化的可能性。
精选毕设项目-体育新闻赛事数据
chromedriver-linux64_122.0.6254.0
基于FPGA的硬件电子琴设计(文档+程序)
在科技与司法的交响曲中,智慧法院应运而生,成为新时代司法服务的新篇章。它不仅仅是一个概念,更是对法院传统工作模式的一次深刻变革。智慧法院通过移动信息化技术,为法院系统注入了强大的生命力,有效缓解了案多人少的矛盾,让司法服务更加高效、便捷。 立案、调解、审判,每一个阶段都融入了科技的智慧。在立案阶段,智慧法院利用区块链技术实现可信存证,确保了电子合同的合法性和安全性,让交易双方的身份真实性、交易安全性得到了有力见证。这不仅极大地缩短了立案时间,还为后续审判工作奠定了坚实的基础。在调解阶段,多元调解服务平台借助人工智能、自然语言处理等前沿技术,实现了矛盾纠纷的快速化解。无论是矛盾类型的多元化,还是化解主体的多元化,智慧法院都能提供一站式、全方位的服务,让纠纷解决更加高效、和谐。而在审判阶段,智能立案、智能送达、智能庭审、智能判决等一系列智能化手段的应用,更是让审判活动变得更加智能化、集约化。这不仅提高了审判效率,还确保了审判质量的稳步提升。 更为引人注目的是,智慧法院还构建了一套完善的执行体系。移动执行指挥云平台的建设,让执行工作变得更加精准、高效。执行指挥中心和信息管理中心的一体化应用,实现了信息的实时传输和交换,为执行工作提供了强有力的支撑。而执行指挥车的配备,更是让执行现场通讯信号得到了有力保障,应急通讯能力得到了显著提升。这一系列创新举措的实施,不仅让执行难问题得到了有效解决,还为构建诚信社会、保障金融法治化营商环境提供了有力支撑。智慧法院的出现,让司法服务更加贴近民心,让公平正义的阳光更加温暖人心。
摘 要 现代经济快节奏发展以及不断完善升级的信息化技术,让传统数据信息的管理升级为软件存储,归纳,集中处理数据信息的管理方式。本线上辅导班系统就是在这样的大环境下诞生,其可以帮助管理者在短时间内处理完毕庞大的数据信息,使用这种软件工具可以帮助管理人员提高事务处理效率,达到事半功倍的效果。此线上辅导班系统利用当下成熟完善的SSM框架,使用跨平台的可开发大型商业网站的Java语言,以及最受欢迎的RDBMS应用软件之一的Mysql数据库进行程序开发。实现了用户在线选择试题并完成答题,在线查看考核分数。管理员管理字典管理、论坛管理、公开课管理、课程管理、课程报名管理、课程收藏管理、课程留言管理、师资力量管理、用户管理、管理员管理等功能。线上辅导班系统的开发根据操作人员需要设计的界面简洁美观,在功能模块布局上跟同类型网站保持一致,程序在实现基本要求功能时,也为数据信息面临的安全问题提供了一些实用的解决方案。可以说该程序在帮助管理者高效率地处理工作事务的同时,也实现了数据信息的整体化,规范化与自动化。 关键词:线上辅导班系统;SSM框架;Mysql;自动化