Property Spec (draft 3)

Forewords

"when I design a class, I think of properties, not getters and setters. I want that intent reflected in the code. I don't want to piece together a dozen lines of getter/setter code, checking that all the names and parameters are just so."
Cay Horstmann

"Over the years I've written a lot of Components and POJOs and thus I've written even more of these property access methods. There are without a doubt certain patterns that are recurring. Recurring patterns means boring boiler plate code, and that's a bad thing, especially since these tend to be hard to refactor to a utility method."
Mikael Grev

"Currently, many many tools, especially frameworks, have to access beans in a dynamic way. By this I mean where a String property name is used to identify a property and reflection is used to access it. But this design is really utter madness!!! A key strength of Java is static typing, allowing compile time detection of errors and refactoring. With a design like this, all these features of Java are lost. "

Stephen Colebourne


"Property Literals (aka Property References, etc) which I think is a really, really great thing to be thinking about."

Richard Bair


"The mighty buffalo of the Java Beans boilerplate animal kingdom are bound properties. They're the kind of properties we write so that our beans can be automatically and dynamically synchronized with a GUI or with each other."

Hans Muller


Why adding property support in Java ?

Property is a well know design concept widely used but that don't have any support in Java language.
Currently the way to represent a property is to use the getter/setter pattern defined by the Bean Specification
and to rely on bean Introspector to obtain an object describing the property (a PropertyDescriptor).

This pattern has many problems, such as:

Property Syntax

A type that defines at least a property is called a bean type.
Allowed bean types are classes, abstract classes, interfaces and enum types.
It is a compile-time error to declare a property in an annotation type.
 
A property is declared using the following syntax:
MethodOrFieldDecl:
Type Identifier MethodOrFieldRest
property Type Identifier {[]} {bound} {Accessor}

Accessor:
get {GetRest}
set {SetRest}

GetRest:
MethodBody {set SetRest}
set

SetRest:
( [final] [Annotations] Type VariableDeclaratorId {[]} ) MethodBody
 

'Property', 'get', 'set' and 'bean' are not real keyword but local keyword. They are recognized
not as keyword but as identifier by the scanner and their value are checked during the parsing.
So 'property', 'get', 'set 'and 'bean' can freely be used anywhere in a code as identifier.

Open issues :

Examples:
 public class MyBean {
public
property String name1; // read-write
public property String name2 get; // read-only
public property String name3 set; // write-only
public property String name4 bound; // bound
  public property String name5 get set; // illegal

public property String name6 get { return "hello"; }; // read-only
public property String name7 set(String name) { }; // write-only

private boolean isReal;
public
property String real
get {
return String.valueOf(isReal);
}
set(String real) {
isReal= String.decode(real);
};
// read-write
}

A compile time error occurs when accessing to a property symbol in its getter or setter.
The property symbol may not appear in the symbol table.
     public property String name8 get { return name8; }; // illegal
Discussion:
  this rule exists to avoid beginners to create infinite recursive accessors.

Modifiers

Properties like any other members allow one of the following access modifiers (jls3 6.6) :
  public, private,  package visible or protected.

Abstract or native property:
  A property can be declared abstract or native.
  These keywords are transferred on generated getter/setter.
  A compile-time error occurs if the property is declared with getter or setter body.

Volatile and transient property:
  A property can be declared volatile or transient.
  These keywords are transferred on generated field.
  A compile-time error occurs if the property is declared with getter or setter body.

Strictfp, synchronized, final or static property:
  A property can be declared strictfp , synchronized, final or static.
  These keywords are transferred on getter/setter (generated or not).

Accessing to a property

A property like a field is accessible using the dot ('.') operator.
 MyBean bean = new MyBean();
bean.name3 = bean.name2;
But unlike field, property access are translated to method call:

Accessing to a property in a constructor:
if the property is not private or final.

There is a snag with constructor and property access. If a property is not private or final, an access
to a property in a constructor is a call to a potentially overridable method in a constructor
which can lead to a publication of a not fully initialized this.

It's a compile-time error to declare a bound property without a method
<T> void propertyChanged(Property<beanType, ?> prop, T oldValue, T newValue)
accessible in the bean class or in its supertypes.

It's a compile time error to:

Open Issue:
  Should we support indexed properties ?

Abstract properties, inheritance and overriding rules


Like methods, properties can be abstract:

Like methods, properties can be overriden if the following rules hold:

The property object

A property object (or property literal, property reference) is a runtime object that
stands for a property and allows to query or modify the value of a property.

A property object is accessible using '#' (sharp), with the following grammar:
IdentifierSuffix:
[ ( ] {[]} . class | Expression ])
Arguments
. (
class | ExplicitGenericInvocation | this | super Arguments |
new [NonWildcardTypeArguments] InnerCreator )
# Identifier

This object is a subtype of java.lang.Property and is parametrized
by the type of the bean type and the type of the property.

By example if the bean Author declares a property named lastname of type String,
Author#lastname is an instance of java.lang.Property typed Property<Author, String>
and its method get and set allows to query/modify the value of a bean object
taken as argument.
 public class Author {
property String name;
property String lastname;
...
}
...
Author author = new Author("joe", "forax");
Property<Author, String> name = Author#name;
name.set(author, "rémi");
String myname= Author#lastname.get(author);

Property object are interned, it is legal to test the identity of a properties using ==
 Author#name == Author#name // is true

Open issue:


Proposed code for java.lang.Property:
package java.lang;

public abstract class Property<B, T> {
    protected Property(int flags, String name) {
        ...
    }

    public abstract T get(B bean);
    public abstract void set(B bean, T value);

    public final property String name get;
    public final property Class<T> type get {
        ...
    }
    public final property Class<B> declaringType get {
        ...
    }
 
    public final property boolean bound get {
        ...
    }
    public final property boolean getterOnly get {
        ...
    }
    public final property boolean setterOnly get {
        ...
    }
}

Open issue:
  Should class Property belong to package java.lang or java.lang.reflect ?

Discussion:
  Why use java.lang.Property and don't reuse java.beans.PropertyDescriptor ?
  Because PropertyDescriptor:

Property object and switch

Properties can be used like enums in a switch statement.
SwitchStatement:
switch ( Expression ) SwitchBlock

SwitchLabel:

case ConstantExpression :
case EnumOrPropertyConstantName :
default :

EnumOrPropertyConstantName:
Identifier

The type of the Expression can be a subtype of java.lang.Property<? extends BeanType, ?>
where BeanType is a specific type.
It's a compile-time error if a case label is not a property declared in BeanType.

 Bean bean=new Bean();
bean.addPropertyListener(new PropertyListener<Bean>() {
public <T> propertyChanged(Property<Bean, T> property, T oldValue, T newValue) {
switch(property) {
case name:
frame.setTitle((String)newValue);
break;
case value:
slider.setValue((Integer)newValue);
break;
}
}
}

Like enums, case items are not qualified. The compiler verifies that the properties Bean.name and Bean.value exist.


Open issue:

this feature will be withdrawn if bug 5029289 (switch on strings) is not implemented.



Translation and backward compatibility

The translation by the compiler needs to be binary compatible with code written using the Bean pattern.
Translations rules:
in order to avoid conflicts, the compiler could append any '$'.

 public class Author {
public property String firstname get {
return fullname.split(" ")[0];
};
public property String fullname bound;

private <T> void propertyChanged(Property<Author, ?> property, T oldValue, T newValue) {
System.out.println(property.name+" "+oldValue+" "+newValue);
}

}
a possible translation (written in Java code):
 public class Author {
public String firstname$get() {
return getFullname().
split(" ")[0];
}
public static class Author$1 extends Property<Author, String> {
Author$1() {
super(GETTER_ONLY, "name");
}
public String get(Author author) {
return author.firstname$get();
}
public void set(Author author, String name) {
throw new UnsupportedOperationException();
}
public static final
Author$1 INSTANCE = new Author$1();
}
private String fullname;
public String fullname$get() {
return fullname;
}
public void fullname$set(String fullname) {
String oldValue = this.fullname;
this.fullname = fullname;

propertyChanged(Author$2.INSTANCE, oldValue, fullname);
 }
public static class Author$2 extends Property<Author, String> {
Author$2() {
super(0, "fullname");
}
public String get(Author author) {
return author.fullname$get();
}
public void set(Author author, String fullname) {
author.fullname$set(fullname);
}
public
static final Author$2 INSTANCE = new Author$2();
}
private <T> void propertyChanged(Property<Author, ?> property, T oldValue, T newValue) {
System.out.println(property.getName()+" changed "+oldValue+" "+newValue);
}

}
Open issue:

The Properties Attribute

In order to enable cross-compilation a new class level attribute named Properties
is needed.

Properties_attribute {
  u2 attribute_name_index;
  u4 attribute_length
  u2 number_of_properties;
  { u2 property_name_index;
     u2 property_access_flags;
     u2 property_getter_name_info_index;
     u2 property_setter_name_info_index;
  } properties[number_of_properties];
}

The items of the Properties_attribute structure are as follows:
attribute_name_index
The value of the attribute_name_index item must be a valid
index into the constant_pool table. The constant_pool entry
at that index must be a CONSTANT_Utf8_info (§4.5.7) structure
representing the string "Properties".

attribute_length
The value of the attribute_length item indicates the length of
the attribute, excluding the initial six bytes.

number_of_properties
The value of the number_of_properties item indicates the number
of entries in the properties array.

properties[]
Each properties array entry contains the following four items:

property_name_index
The value of the property_name_index item must be a valid index into the
constant_pool table, and the entry at that index must be a
CONSTANT_Utf8_info (§4.5.7) structure that represents the
original simple name of the property, as given in the source code from
which this class file was compiled.

property_access_flags
The value of the property_class_access_flags item is a
mask of flags used to denote access permissions to and
properties of the current property as declared in the source
code from which this class file was compiled. It is used by
compilers to recover the original information when source
code is not available.

Flag Name
Value
Meaning
ACC_PUBLIC
0x0001
Marked public in source
ACC_PRIVATE
0x0002
Marked private in source
ACC_PROTECTED
0x0004
Marked protected in source
ACC_STATIC
0x0008
Marked static in source
ACC_FINAL
0x0010
Marked final in source
ACC_SYNCHRONIZED
0x0020
Marked synchronized in source
ACC_VOLATILE
0x0040
Marked volatile in source
ACC_TRANSIENT
0x0080
Marked transient in source
ACC_NATIVE
0x0100
Marked native in source
ACC_ABSTRACT
0x0400
Marked abstract in source
ACC_STRICT
0x0800
Marked strictfp in source
ACC_USER_DEFINED
0x2000
User defined property
ACC_BOUND
0x4000
Marked bound in source


property_getter_name_info_index
If the property has no getter, the value of the
property_getter_name_info_index item must be zero. Otherwise
it must be a valid index into the constant_pool table, and the entry at that index
must be a CONSTANT_Utf8_info (§4.5.7) structure that represents the
name of the getter of the property given by the compiler.

property_setter_name_info_index
If the property has no setter, the value of the
property_setter_name_info_index item must be zero. Otherwise
it must be a valid index into the constant_pool table, and the entry at that index
must be a CONSTANT_Utf8_info (§4.5.7) structure that represents the
name of the setter of the property given by the compiler.


java.lang.Class update


java.lang.Class need to be updated by adding to new methods specified as follow:
/**
* Returns an array containing the properties declared by this
* type. This method may be used to iterate over the properties
* as follows:
*
* Bean bean = ...
* for(Property<Bean, ?> prop: bean.getClass().getProperties())
* System.out.println(prop.name + " = "+ prop.get(bean));
*
* @return an array containing the properties of this type
* or an empty array if the current type doesn't define any
* property.
*/
public Property<Bean, ?>[] getProperties();


/**
* Returns a properties of this type with a specified name.
* The string must match an identifier used to declare a property without extraneous whitespace.
*
* @param name the name of the property to be returned.
* @return a property with the specified name
* @throws IllegalArgumentException if this type has no property with the specified type.
* @throws NullPointerException if name is null.
*/
public Property<Bean, ?> getProperty(String name);


The signature of the method getProperties is not currently legal, technically it's legal
but unsafe, but we hope this signature will be legal at the time properties will be
included into the spec.

JSR 199 and properties

TBF (two new nodes Property and SharpAccess)

Javadoc

Javadoc needs to be updated to nicely display properties

Bean spec update

The bean spec should be updated to rely on property instead of String and reflection.
By example by:

public class PropertyChangeEvent {
    public PropertyChangeEvent(Object source, String propertyName, Object oldValue, Object newValue) {
        super(source);
        this.propName = propertyName;
        this.oldValue = oldValue;
        this.newValue = newValue;
    }

    /**
     * @since 1.7
     */
    public PropertyChangeEvent(Object source, Property<?, ?> property, Object oldValue, Object newValue) {
        super(source);
        this.property = property;
        this.oldValue = oldValue;
        this.newValue = newValue;
    }

     private Property<?, ?> prop;
     /**
       * @since 1.7
       */
     public property Property<?, ?> property get {
         if  (prop != null)
             return prop;
         return prop = getSource().getClass().getProperty(propertyName);
     };
     private String propName;
     public property String propertyName get {
         if (propName !=null)
             return propName;
         return prop.name;
     };
     public property oldValue get;
     public property newValue get;
}