Atom Feed SITE FEED   ADD TO GOOGLE READER

Extensible Properties for the JVM, refined

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:
  1. 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.
  2. 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.
  3. Properties should not increase per-instance memory usage.
  4. Properties should have a minimal impact on performance
  5. Properties should be toolable.
  6. 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 a Property 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);
}
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 the property 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 first
It'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.
This design is along the lines of my thoughts too. However, I disagree wit h the setName/setDelegate on the Property interface. These could allow application code to change the definition of the property. A better approach would be to say that a property must have a constructor that takes a Property. Or possibly a separate DecoratorProperty interface.

You can also shorten the property creation:
private property(ObservableProperty, NonNullProperty) String bar = "phils";
I agree that allowing the Property literals to be mutable could be problematic.

Here's something I should be possible with Property decorators, but that requires the setDelegate() method:
private Property(new TruncateStringProperty(255), PropertyFactory.remapProperty(null, "")) String bar = "phils";