The debate around properties is hotting up.
I'm going to try and contribute by defining a common terminology for everyone to communicate in.
Properties terminology
This is a classification of the three basic property types being debated.
Names are given for each type of property.
My hope is that the names will become widely used to allow everyone to explain their opinions more rapidly.
Note that although none of the examples show get/set methods,
all of the options can support them.
It should also be noted that the interface/class definitions are reduced and don't include
all the possible methods.
Type 1 - Bean-independent property
(aka per-class, property-proxy, property adaptor)
This type of property consists of a type-safe wrapper for a named property on a specific
type of bean.
No reference is held to a bean instance, so the property is a lightweight singleton, exactly
as per Field
or Method
.
As such, it could be held as a static constant on the bean.
public interface BeanIndependentProperty<B, V> {
/**
* Gets the value of the property from the specified bean.
*/
V get(B bean);
/**
* Sets the value of the property on the specified bean.
*/
void set(B bean, V newValue);
/**
* Gets the name of the property.
*/
String propertyName();
}
This type of property is useful for meta-level programming by frameworks.
The get and set methods are not intended to be used day in day out by most application developers.
Instead, the singleton bean-independent property instance is passed as a parameter to
a framework which will store it for later use, typically against a list of actual beans.
A typical use case would be defining the properties on a bean to a comparator,
for example comparing surnames, and if they are equal then forenames.
Clearly, the comparator needs to be defined completely independently to any actual bean instance.
Comparator<Person> comp = new MultiPropertyComparator<Person>(
Person.SURNAME, Person.FORENAME // bean-independent property defined as static constant
);
Bean-independent properties can be implemented in two main ways.
The first option uses reflection to access the field:
public class Person {
public static final BeanIndependentProperty<Person, String> SURNAME =
ReflectionIndependentProperty.create(Person.class, "surname");
private String surname;
}
The second option uses an inner class to access the field:
public class Person {
public static final BeanIndependentProperty<Person, String> SURNAME =
new AbstractIndependentProperty("surname") {
public String get(Person person) { return person.surname; }
public void set(Person person, String newValue) { person.surname = newValue; }
};
private String surname;
}
As bean-independent properties are static singletons, they should be stateless and immutable.
Type 2 - Bean-attached property
(aka per-instance)
This type of property consists of a type-safe wrapper for a named property on a specific
instance of a bean.
As the bean-attached property is connected to a bean, it must be accessed via an instance method.
public interface BeanAttachedProperty<B, V> {
/**
* Gets the value of the property from the stored bean.
*/
V get();
/**
* Sets the value of the property on the stored bean.
*/
void set(V newValue);
/**
* Gets the name of the property.
*/
String propertyName();
}
This type of property is useful for frameworks that need to tie a property on a specific bean
to a piece of internal logic.
The most common example of this is many parts of the Beans Binding JSR, such as textfield bindings.
The get and set methods are not intended to be used day in day out by most application developers.
Instead, the bean-attached property instance is passed as a parameter to a framework
which will store it for later use.
A typical use case would be defining the binding from a person's surname to a textfield.
In this scenario we are binding the surname on a specific bean to the textfield on a specific form.
bind( myPerson.surnameProperty(), myTextField.textProperty() );
Bean-attached properties can be implemented in two main ways.
The first option uses reflection to access the field.
This example show the attached property being created new each time (on-demand),
however it could also be cached or created when the bean is created:
public class Person {
public BeanAttachedProperty<Person, String> surnameProperty() {
return ReflectionAttachedProperty.create(this, "surname");
}
private String surname;
}
The second option uses an inner class to access the field.
Again, this example show the attached property being created new each time (on-demand),
however it could also be cached or created when the bean is created:
public class Person {
public BeanAttachedProperty<Person, String> surnameProperty() {
return new AbstractAttachedProperty("surname") {
public String get() { return surname; }
public void set(String newValue) { surname = newValue; }
};
private String surname;
}
Bean-attached properties are merely pointers to the data on the bean.
As such, they should be stateless and immutable.
Type 3 - Stateful property
(aka bean-properties, property objects, Beans 2.0, Eclipse IObservableValue)
Note that stateful properties have been implemented in the bean-properties project, however that project
goes well beyond the description below.
This type of property consists of a stateful property on a specific instance of a bean.
The property is a fully fledged object, linked to a specific bean, that holds the entire state
of the property, including it's value.
This approach is based around an alternative to the standard beans specification, however
get/set methods can be added if desired.
public class StatefulProperty<B, V> {
private B bean;
private V value;
/**
* Constructor initialising the value.
*/
StatefulProperty<B, V>(B bean, V initialValue) {
this.bean = bean;
value = initialValue;
}
/**
* Gets the value of the property from the stored bean.
*/
public V get() { return value }
/**
* Sets the value of the property on the stored bean.
*/
public void set(V newValue) { value = newValue; }
/**
* Gets the name of the property.
*/
public String propertyName();
}
This type of property is intended for application developers to use day in day out.
However it is not intended to be used in the same way as normal get/set methods.
Instead, developers are expected to code a bean and use it as follows:
// bean implementation
public class Person {
public final StatefulProperty<Person, String> surname =
new StatefulProperty<Person, String>(this, "surname");
}
}
// usage (instead of get/set)
String s = myPerson.surname.get();
myPerson.surname.set("Colebourne");
The important point to note is that the surname field on the bean is final and a reference
to the stateful property. The actual state of the property is within the stateful property,
not directly within the bean.
A stateful property fulfils the same basic API as a bean-attached property (type 2).
As such in can be used in the same use cases.
However, because a stateful property is an object in its own right, it can have additional
state and metadata added as required.
The key difference between bean-attached (type 2) and stateful (type 3) is the location
of the bean's state. This impacts further in that bean-attached properties are intended
primarily for interaction with frameworks, whereas stateful properties are intended for everyday use.
Language syntax changes
Both bean-independent (type 1) and bean-attached (type 2) properties benefit from language syntax change.
This would be used to access the property (not the value) to pass to the
framework in a compile-safe, type-safe, refactorable way:
// bean-independent - accessed via the classname
BeanIndependentProperty<Person, String> property = Person#surname;
// bean-attached - accessed via the instance
BeanAttachedProperty<Person, String> property = myPerson#surname;
Stateful properties (type 3) do not need a language change to access the property as they are designed around it, making it directly available and fully safe.
The second area of language change is definition of properties.
This is a complicated area, which I won't cover here, where all three types of property would benefit from language change.
The third area of language change is access to the value of a property.
Again, there are many possible syntaxes and implications which I won't cover here.
Combination
The full benefit of bean-independent (type 1) and bean attached (type 2) properties
occurs when they are combined.
Additional methods can be added to each interface to aid the integration, and any
language syntax change becomes much more powerful.
Summary
I hope that these definitions will prove useful, and provide a common terminology for
the discussion of properties in Java.
Opinions are welcome, and I will make tweaks if necessary!