PUBLIC OBJECT

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);
    }</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&lt;Foo,String&gt; $PROPERTY_bar = new ObservableProperty();
    static {
        Property&lt;Foo,String&gt; p1 = new NonNullProperty();
        Property&lt;Foo,String&gt; p2 = new DefaultProperty&lt;Foo,String&gt;() {
            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&lt;Foo,String&gt; $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&lt;Foo,String&gt; 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.