General
In most Java projects you have a
lot of classes written only for the
purpose of holding some data.
If you need a bean class which can
hold an int, a String and an
instance of Foo you have to implement the following class:
public class
BadExample {
private int intValue;
private String stringValue;
private Foo foo;
public Foo getFoo() {
return this.foo;
}
public void setFoo(Foo foo) {
this.foo = foo;
}
public int getIntValue() {
return this.intValue;
}
public void setIntValue(int intValue) {
this.intValue = intValue;
}
public String getStringValue() {
return this.stringValue;
}
public void setStringValue(String stringValue) {
this.stringValue = stringValue;
}
}
Of course you could use a
Map/HashMap to hold the data but you would
lose type-safety (and running into the problems of synchronisation of
the map): Why not let Java
do this boring
job? Use Java
Bean Proxy!
You only have to define the
interface:
public
interface NiceExample {
Foo getFoo();
void setFoo(Foo foo);
int getIntValue();
void setIntValue(int intValue);
String getStringValue();
void setStringValue(String stringValue);
}
The rest is done by Java Bean
Proxy. To get an instance use
BeanProxyBuilder:
NiceExample
niceExample =
BeanProxyBuilder.on(NiceExample.class).build();
niceExample.setIntValue(44);
System.out.println(niceExample.getIntValue()); //
Will of course result in "44"
BeanProxyBuilder will
return a dynamic (runtime) instance
for NiceExample.class which implements hashcode, equals and toString
correctly Additionally this runtime instance is clone- and
serializable.
Initialzation
If you'd like to initialize your
beans (by default all object values are null and primitives 0/false)
you can annotate them using @Initializer and set the attribute to the
class that should be initialize the bean:
@Initializer(NiceExampleInitializer.class). The referenced class has to
implement exactly one public static method that accepts instances of
the bean class (NiceExample), e.g.
public static void initialize(NiceExample
bean) {
bean.setStringValue(""); //
return empty strings instead of null
}
The proposed way to use this is
creating an inner class:
@Initializer(Foo.Init.class)
public static interface Foo {
static class Init {
public static void initialize(Foo foo) {
foo.setBar("");
}
}
[...]
}
PropertyChangeSupport
If you need PropertyChangeSupport (or even VetoableChangeSupport) all
you have to do is let your interfaces extend
DefaultPropertyChangeEventProvider (and/or
DefaultVetoablePropertyChangeEventProvider if you like to support vetos)
public
interface NiceExample extends DefaultPropertyChangeEventProvider {
[...]snipped[...]
}
DefaultPropertyChangeEventProvider
declares two methods:
- addPropertyChangeListener(java.beans.PropertyChangeListener)
- removePropertyChangeListener(java.beans.PropertyChangeListener)
so everyone having a reference to
NiceExample can register/deregister listeners using these two methods.
If you come with your own interface declaring add and remove methods
you can still use them, simply annotate them with
@PropertyChangeEventLinker, if your add/remove have other names than
add* and remove* you can use PropertyChangeEventLinker's attributes:
@PropertyChangeEventLinker(addMethod = "nameOfAddMethod", removeMethod
= "nameOfRemoveMethod")
If modifications of several
attributes should not fire PropertyChangeEvents you can do this
annotating the attributes with these annotations:
- @IgnoreVeto: PropertyChangeEvents
will be fired but the modification cannot be interrupted by the
listener (the VetoException thrown by the listener will be ignored)
- @Unbound: No
PropertyChangeEvent will be fired
http://sourceforge.net/projects/beanproxy/
Have a lot of fun!