Remi and Richard are talking about properties again, refining their ideas. I figured I should organize my thoughts as well.
FIrst off, how about some aggressive requirements:
-
Properties should support arbitrary implementations! It should be possible to have properties backed by fields, HashMaps, databases, calculations, devices etcetera. Using such a property should require minimal amounts of code.
-
Properties should support pluggable behaviours. Properties should be able to be made Observable, non-nullable, validated, copied-on-write, etcetera. Using such a behaviour should require minimal amounts of code.
-
Properties should not increase per-instance memory usage.
-
Properties should have a minimal impact on performance
-
Properties should be toolable.
-
Properties should interact cleanly with legacy getters and setters.Just as the enhanced for loop works using the
Iterable
interface, I think the properties for the JVM should have aProperty
interface like this:public interface java.lang.Property<B,V> {
void setName(String name);
void setDelegate(Property other);
void set(B bean, V value);
V get(B bean);
}</pre>Note that this interface provides a _delegate_ to allow
Properties` to be chained. This allows behaviours like observable to be stacked.When defining a property, the coder provides a list of properties objects to be chained in varargs style:
`class Foo { private property(new ObservableProperty(), new NonNullProperty()) String bar = "phils"; }`
...is syntactic sugar for:`class Foo {
private String bar = "phils"; private static Property<Foo,String> $PROPERTY_bar = new ObservableProperty(); static { Property<Foo,String> p1 = new NonNullProperty(); Property<Foo,String> p2 = new DefaultProperty<Foo,String>() { public String get(Foo foo) { return bar; } public String set(Foo foo, String bar) { foo.bar = bar; } }; p1.setDelegate(p2); $PROPERTY_bar.setDelegate(p1); }
}`
Let's disect the generated code:
private static Property<Foo,String> $PROPERTY_bar
The property literal is a static member of the class. The property is static so we don't increase memory usage per-instance. The name starts with a$
dollar sign to denote the field is compiler-generated and cannot be referenced normally.*= new ObservableProperty()
The property literal instance is the outermost decorated value. This code is the first argument after theproperty
token in the original source.static { Property<Foo,String> p1 = new NonNullProperty(); p2 = ...
Properties are constructed in order, from outermost to innermost. They're created in a static initializer because the property literals are shared between all instances.= new DefaultProperty{Foo,String}() { ... }
Default code to retrieve the field is generated. Because actual bytecode is created, field access is significantly faster than reflection.p1.setDelegate(p2);
The chain of property literals is linked together, innermost firstIt's not as interesting to me, but it's also straightforward to create syntactic sugar for accessing properties:` Foo foo = new Foo(); foo**#**bar = "bomber"; System.out.println(foo**#**bar);`
... and property literals` Property<Foo,String> barProperty = Foo**##**bar;
barProperty.set(new Foo(), "revolution"); System.out.println(barProperty.get(new Foo()))
Noting of course that the symbols #
and ##
are totally arbitrary and could be exchanged with any symbols, such as :
, $
, []
. I think that multiple symbols are necessary to differentiate between a particular instance's property value and a property literal, otherwise the symbol #bar
is ambiguous inside the Foo.java source file.