Atom Feed SITE FEED   ADD TO GOOGLE READER

Extensible Properties for the Java Language

Remi Forax is leading some research on hacking in properties into the JVM. Richard Bair's latest post has inspired me to write about how I think observable properties could be implemented.

I would like properties to be extensible, so that all the generic boilerplate (not just get/set) can be applied:
public class Person {
private @Property(ObservableProperty.class) String surname;
}

...where ObservableProperty implements a chain-of-command interface for setting properties:
interface Property<B,T> {
/** the delegate holds the 'real' property that we're just decorating */
public void setDelegate(Property<B,T> delegate);
/** @param name the bean property name, such as surname */
public void setName(String name);
public T get(B bean);
public void set(B bean, T value);
}

... and ObservableProperty is implemented something like this:
public class ObservableProperty<B extends Observable,T> implements Property<B,T> {
private Property<B,T> delegate;
private String name;
public void setDelegate(Property<B,T> delegate) {
this.delegate = delegate;
}
public void setName(String name) {
this.name = name;
}
public T get(B bean) {
return delegate.get(bean);
}
public void set(B bean, T value) {
T old = delegate.get(bean);
delegate.set(bean, value);
firePropertyChange(bean.getListeners(), name, old, get(bean));
}
}


The real power of this approach is chaining decorators:
   private @Property(ObservableProperty.class, NotNullProperty.class) String surname;
private @Property(ObservableProperty.class, EmailAddressProperty.class) String emailAddress;
private @Property(TransactionalProperty.class) long id;


This approach makes code better because it is declarative. We are telling the API what to do, not how to do it.
How does this work for properties that are read-only, or write-only? In particular, will the compiler be able to tell?

How does it work when a set fails (validation), or a get fails (suppose it doesn't want to return null).

When I posted a similar idea in a comment on one of Rémi's posts, I made get and set able to fail and communicate failure to the caller. Perhaps exceptions would be doable instead.

Rémi preferred an instance instead of ObservableProperty.class, presumably so that one can change the behaviour of a property at runtime. That's still possible with a class anyway.

You're onto a non-starter if you use annotations for language features in Java - they are not supposed to affect the operation of code at all. That's a great way to get your proposal rejected.
Sure, there's a lot of simplifications here. Perhaps I could have 3 interfaces:
interface ReadableProperty<B,P> {
P get(B base);
}
interface WritableProperty<B,P> {
void set(B base, P value);
}
interface Property extends ReadableProperty, WritableProperty {
// no new methods
}

What to do when a set fails? Throw an IllegalArgumentException just like we do today with setters. This is a rare situation, so RuntimeExceptions are fine. If there's user-input validation necessary, some other mechanism should be used.

And as for annotations, you're right, it is a stated goal that annotations are not executable. It's totally reasonable to change the syntax for declaring a property:
private property(new ObservableProperty(), new NotNullProperty()) String foo;
I'd still like to allow checked exceptions - runtime exceptions are poor in the context of a statically typed language.
Allowing checked exceptions sounds like a good idea but it won't work for property literals.

Property should be an interface, and no implementations can throw more exceptions than those defined by the interface.

The interface shouldn't define any checked exceptions since there is absolutely nothing intelligent the caller can do when receiving such an exception.

I don't think that checked exceptions are very common for bean fields, and I'd happily give them up for property literals.
An interface can use generics to define other exceptions. The only issue is that there is no varargs-like way of allowing a method to throw an use-site-dependent number of exceptions. I believe that the closures proposal will be addressing that. Anyway, the single-exception way:

interface Property<T,E1 extends Exception,E2 extends Exception>
{
T get() throws E1;
void set(T t) throws E2;
}

There is no way, in Java 6, of not specifying E when using the above interface. The closure proposal introduces the null type, which will allow this.

There are some cases where there is something intelligent to do when receiving an exception from a getter or setter.

"I don't think that checked exceptions are very common for bean fields, and I'd happily give them up for property literals."

It's not an either/or - it's trivial to have both, and in a statically typed language, it's a failure of language design not to have static exception checking.
Is any of this possible today? I've never seen an interface where the exception types are generic. Do you have an example?

If it's possible, I'm interested.

I still don't think this can work for a generic binding system - what exception would it catch?
Yes, declaring a method that throws E, where E is a type parameter that extends Exception (or Throwable, etc.), is allowed.

Here's an example - a generic Function interface, which models a function that has one input, one output, and may throw one exception:

interface Function<T,R,E extends Exception>
{
    R invoke(T input) throws E;
}

The generic binding system would catch E - this is the bit that isn't possible today, because of erasure.