JPA (Hibernate)
@Id
public property long id get;
@Column("FIRST_NAME")
public property String firstName;
new Criteria(Person#firstName, value);
Validations
protected <B,T> void propertyChanged(
java.lang.Property<B,T> property,
Object oldValue,Object newValue) {
// Activate validation rules and so stop setting the value
}
WebBeans (JSR-299) or Guice
@Log
private static property Logger log;
Wicket or Swing
new TextField(Person#firstName);I'd like to say that Remi's work is truly amazing and is a good first draft. It is open source for us to analyze and contribute to. The benefits of properties in the cases above are:
new DynamicPanel().addAll(Person.properties());
- No issue of annotations on either a field and/or getter/setter methods. All is centralized on one property declaration.
- No reflection. All method calls from and to the framework are compiled and wired without using reflection. This is a great boost for UI and Dependency Injection framework.
- Introspection without reflection.
- The first problem I encountered is that writing get and set blocks inline looks really good but breaks Object Orientation. I cannot do inheritance or use polymorphisms in these blocks. I ended up having more boilerplate code than without the usage of properties.
- The second is that the private field exists only if there is no get/set block. So, if you wish to write getter/setter that uses a field, you need to declare this field. Suddenly, a lot of the generated syntactic sugar disappears and you find yourself writing almost as much as before.
- The third issue is that encapsulation is asymmetric. What I mean by that is System.out.println(firstName) will use directly the field, but this.firstName = "toto" will become this.setFirstName("toto");
So, I tried to see how I can solve these limitations. Since modularity is the most important feature for me, a clean solution for the DI framework property issue is critical. This can really turn Java into a true Component based platform at its root. So as for the Logger issue I actually want to write:
private static property Logger logI added here some init block in the same line as the get/set block, since this is really what I want - to declare how the field is initialized. But, I'd like to write this once and reuse it. Furthermore, I want the init to be executed when the field (either static or instance) is declared, or on demand (lazy init). The only good way to do it is to have a class wrapping the Type2 property. It would look something like:
init {log = Logger.getLogger(Person.class.getName());};
public class Person {Of course it is missing a lot of good (or not) synchronized, but the idea is here. Basically I ended up with the Joda Beans or java.net bean properties concept, and also Yardena's idea.
public final static Property<Logger> log = new LazyInitProperty() {
protected void init() {
value = Logger.getLogger(Person.class.getName());
}
};
public final Property<String> lastName = new Property<String>();
}
public class Property<T> implements BaseProperty<T> {
private T value;
public T get() {return value;}
public void set(T value) {this.value = value;}
}
public abstract class LazyInitProperty<T> implements ReadOnlyProperty<T> {
protected T value;
public T get() {
if (value == null)
init();
return value;
}
protected abstract void init();
}
This is an old idea about the notion that properties don't need to be a language feature. Take a look at What Makes Bean-Properties Special, and 2 discussions on The Java Posse Google group: Wonder what Joe thinks of this properties solution, Still hope for first-class properties.
Conclusion
The solution to all of my problems: Properties should not be a language feature!When I got there for the first time I thought: WTF! But that's a logical step, isn't it? Properties are field level encapsulation with accessors and events. You can do this in any OO language with a class wrapping your field. So why are we suffering?
The main problem with open source properties projects is that all the classes and interfaces of PropertyType2<T> and PropertyType1<B,T> belong in the JDK. They need to be made a standard so that all framework and component will use them. To be useful, the bean-properties project needs to be in a JSR and included in Java 7. Why didn't any open source property project became a de-facto standard? It's amazing how many Property classes already exists out there!
- Joda does binding with no boilerplate but changes the getter/setter style.
- Bean-properties does the binding boilerplate code (and even setter/getter) with bytecode generation. Personally I prefer good syntactic sugar.
- Annotations on these specific "Property" fields are not understood by 3rd party framework like JPA.
- You need to declare public final fields.
- Even if the amount of code is quite low, you still repeat yourself in the field declaration.
- Bean introspection and Property object access are done using Strings and reflection.
- The amount of features (Read, Write, initialized, Lazy, bind), types (Simple, Array, Set, Map,...), Modifiers (private, public, synchronized, volatile, for field/getter/setter), are generating a combinator which is impossible to implement with a Property<T> class hierarchy.
So what!
Well, my final wild idea is:- I want a unique Type1 interface in the JDK (like Remi's Property<B,T>) called PropertyAdaptor<B,T> to be separated from bean-properties class. This can (and will in my kijaro branch) be implemented with an abstract enum.
- I want Person#firstName to return the above Type1 enum instance, and then use it to remove the need to change the class format (similar to what needs to be done in Remi's implementation today).
- I want a nice, small and simple Type2 property interfaces and classes hierarchy, that are making a heavy usage of Annotations and the above unique Type1 enum entry for initialization. Basically this means making some "kind of" bean-properties project part of the Kijaro JDK (KDK ;-).
- The Type1 methods "T get(B)" and "void set(B,T)" should delegate (by the javac phase) to "T get()" and "set(T)" of the Type2 instance. This would remove the need for reflection.
- There should be no restrictions on extending classes or interfaces in the JDK based Property<T> (Type2) hierarchy and on using them. This way, WebBeans and JPA can provide state-of-the-art properties implementations tuned for their needs. This would provide the loose coupling and transparent state management at a language level without reflection.
- The property keyword should be replaced by the above annotation. This Annotation will activate all the syntactic sugar that would remove the standard properties boilerplate code. But, most of the actual Type2 implementation will come from the super class and extra annotations.
- Dependency Injection (OSGi), dynamic object allocation and redirection - can be done by calling set on the required properties, by having a smart get or by setting a dirty flag. Whatever the solution be, it is totally transparent to the business code declaring the property field.
- Persistence framework providing validations (from DB type, data and annotations) and listening to property changes.
- Swing bindings of course, but used by higher level framework that can manage full business objects and all their properties.
What I'm gonna' do!
Independently of the wild crazy idea of properties by annotation as a language feature, the property list introspection will be done using a unique abstract enum java.property.PropertyDefinition<B,T>:public abstract enum PropertyDefinition<B, T>And the javac generated code will look like:
implements PropertyAdaptor<B, T> {
public abstract T get(B bean);
public abstract void set(B bean, T newValue);
public abstract Property<T> getProperty(B bean);
public String propertyName() {
return name();
}
public Class<B> getBeanClass() {...}
public Class<T> getPropertyType() {...}
public Annotation[] getAnnotations() {...}
public boolean isReadable() {...}
public boolean isWritable() {...}
public int getFieldModifiers() {...}
public int getGetterModifiers() {...}
public int getSetterModifiers() {...}
}
public class Person {The above work is a simple merge of Remi's implementation and the abstract enum. On top of that, I'll implement the generic Type2 thing. First an annotation needs to be associated with a Property<T> (Type2) abstract class. I'll use a meta-annotation PropertyClass, so anyone can declare a future @Property annotation.
[...]
public static enum properties<T> extends PropertyDefinition<Person,T> {
<String> firstName,
<String> lastName,
<Integer> age
}
}
The meta annotation:
@Retention(RetentionPolicy.RUNTIME)The specific annotation for a basic property:
@Target(ElementType.ANNOTATION_TYPE)
public @interface PropertyClass {
Class value();
}
@Retention(RetentionPolicy.RUNTIME)The specific annotation for a property that can be bound:
@Target(ElementType.FIELD)
@PropertyClass(BasePropertyImpl.class)
public @interface BasicProperty {
PropertyAccess value();
}
@Retention(RetentionPolicy.RUNTIME)Then, the bean class:
@Target(ElementType.FIELD)
@PropertyClass(ObservablePropertyImpl.class)
public @interface ObservableProperty {
// Using FCM
#void(PropertyStateEvent) onSet default null;
}
public class Person {And so the boilerplate code generated by javac will look like:
@BasicProperty(PropertyAccess.READ_WRITE)
private String firstName;
@ObservableProperty(Person#firstNameChanged)
private String lastName;
@BasicProperty(PropertyAccess.READ_ONLY)
private int age;
private void firstNameChanged(PropertyStateEvent se) {
System.out.println("property changed "+se);
}
}
public class Person {And all accessors to person.firstName will be wrapped in javac with person.firstName.set() and person.firstName.get().
@BasicProperty(PropertyAccess.READ_WRITE)
private final BasePropertyImpl<String> firstName =
new BasePropertyImpl<String>(properties.firstName);
@ObservableProperty(Person#firstNameChanged)
private final ObservablePropertyImpl<String> lastName =
new ObservablePropertyImpl<String>(properties.lastName);
@BasicProperty(PropertyAccess.READ_ONLY)
private final BasePropertyImpl<Integer> age =
new BasePropertyImpl<Integer>(properties.age);
private void firstNameChanged(PropertyStateEvent se) {
System.out.println("property changed "+se);
}
public static enum properties<T> extends PropertyDefinition<Person,T> {
<String> firstName,
<String> lastName,
<Integer> age
}
}
This proposition respects Object Orientation, it is very flexible and open for future extension. It removes a lot of boilerplate code and makes Java ready for easy-to-use components. For example, by applying this solution IoC frameworks can really be "inverted". What I mean by that is that instead of trying to invoke setXXX() on the components at the right time (object life cycle and/or component state), they can simply provide a state-of-the-art getter in the property type2 class that will fetch the right object at the right time, all the time. This is transparent decoupling, keeping type safety and without using reflection.
So it solves some (but not all) of the issues like:
- Yardena's need for flexible property behavior and generics issue with properties,
- Eric Burke's ability to have more flexible binding
- My need for using FCM pointer in properties declaration. Which methods init, bind, listen, and so on.
A few more white nights are ahead of me so I can put all this in kijaro, I guess ;-) Unless someone finds a good reason to stop me in my wild Java experience!
12 comments:
It is almost scary how you have arrived at almost exactly the same solution as I have... https://properties.dev.java.net/ was a project a cooked up to be the API part. A JSR should outline the needed syntactic sugar to make it simple to access the properties. Well, still pending approcal, I guess someone at Sun has a lot to do..
That's scary!
This is almost exactly what I need and meant. For this project it's better than bean-properties project.
I will take this for Kijaro if it's OK with you?
Sure, no problem. BSD is a very lenient license so you can do whatever. Please inform me at grev (at) miginfocom (dot) com if you arrive at a solution or so.
I also wrote this a few months ago. It might be interesting to someone: http://www.javalobby.org/java/forums/t102115.html
I am working on an implementation (type 1, according to Mikael's definition) as part of my university project. I haven't uploaded the code to on-line repository yet, but I do have a working version. For some reason I cannot access https://properties.dev.java.net/, but I am very interested in participating in this initiative.
Same here. Everytime I try to access the website, it tells me I need more privileges to the administrator. Is not the properties project public?
I think you just need to be a logged in java.net user to access it.
But the not public issue is the reason why it was not indexed by google and so I did not find it when browsing for Properties implementation ;-)
I am logged in to java.net, but the project is seriously hidden - direct url access gives "no permission" and it does not come up on internal java.net search either.
Ditto. I created a java.net account specifically to access the website after reading this post. As yardena said, it's totally restricted even when logged in.
Hi! The guys of javalobby are of the opinion that there is only "Maybe - 40%" propability that java properties will be implemented in java7.
http://java.dzone.com/news/java-7-predictions
What is your prediction? Thanx!
@Yardena and Anonymous: you were right, Mikael told me that the project was not approved until today. So, it should be OK now. Good luck.
@Ivan: Personally I feel like Alex about this "Maybe - 40%" as properties is presented today: Save the writing of getter/setter.
But when I started playing with javac I linked it to what's coming with Guice integrated in JSR-299. Today, if this idea of having properties to remove the boilerplate of DI is being heard, it may change the properties bad reputation.
Who knows "qui vivra vera"...
"Transforming person.firstName="toto"; to person.firstName.set("toto"); looks very close to the boxing/unboxing feature, doesn't it?"
This has a problem. What does person.firstName return? Is it a Property[String] or a String?
The answer can't be 'whatever is required', as it could be a Property[Property[String]]!
Post a Comment