 |
Property Reloaded
Posted by forax on January 23, 2007 at 08:26 AM | Comments (27)
This entry is the second draft of my property proposal,
i have tried to gather all the ideas proposed since
my first post about properties.
Why do we need a property syntax in Java ?
If you have not followed the different blogs,
i recommend you to first read
properties in Java by Richard Blair
and Properties are design features by Cay Horstmann.
Declare a property
There is three kind of property, simple property that are
translated to a field plus a getter and a setter automatically
generated by the compiler, user-defined property,
a property for which getter and setter are defined by the developer
and abstract property, a property for which getter
and setter are abstract.
All kind of properties can be read-only, write-only
or read-write.
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 get set; // illegal
public property String name5 get { return "hello"; } // read-only
public property String name6 set(String name) { } // write-only
private String _name7;
public property String name7
get { return _name7; }
set(String name) { _name7 = name; firePropertyChange(name7); } // read-write
}
As i have already said, property, get and set are not real keywords
but only local keywords.
A property is implicitly read-write so the syntax for name4
is illegal. All other combinations that the ones above are invalid.
You can also notice that if the property is not abstract and ends
with a semi-colon, a field is created.
Access to a property
Unlike my first proposal, i think now that we must not have
two ways to access to a property. So 'dot' can't be used to
access/change the value of a property
(It has another meaning, see further).
In order to permit retrofitting of already existing code,
properties are accessed using getXXX/setXXX.
It's a little awkward especially for beginners but i really
think that is the best solution.
MyBean bean = new MyBean();
bean.setName1(bean.getName2());
Property Litteral
Because i'm lazy, instead of defending myself property literal,
i prefer to redirect you to
Evan Summers's
property reference
or Stephen Colebourne's
property objects
Matthias Ernst proposed to create a
property object by property and by object. I think
it is not very feasible due to the memory comsumption.
I think a system ala java.lang.reflection, i.e.
one property object by property and not related to an
instance is more realisic.
I propose to use dot on the class to access to such object.
Bean bean = new Bean("joe", "forax");
Property<Bean, String> name = Bean.name;
name.set(bean, "rémi");
Property<Bean, String> lastname = Bean.lastname;
String myname= lastname.get(bean);
A property literal is like .class, it acts like
a static final field.
The compiler translates ClassName.propertyName to
ClassName.class.getProperty("propertyName").
Perhaps the property object can be cached to avoid
one lookup by call like .class in transalted by the compiler
in pre-tiger release.
Property Litteral and generics
The idea is to make Property safe, so a property
is typed using generics. I propose to parametrize a property objet
Property by its bean type and the type of its value.
Property<Bean, String> prop = Bean.name;
Bound Property
A simple property can be bound to make
swing hackers happy.
If such property is defined in the class,
a method propertyChange() is required
in the class or in on of its supertypes.
A bound property is always read-write.
class Point {
public property int x bound;
public property int y bound;
private <T> void propertyChange(Property<Point, T> prop, T oldValue, T newValue) {
// do what you want here
}
}
The compiler translates the bound property x into :
private int x;
public int getX() {
return x;
}
public void setX(int x) {
int oldX=this.x;
if (x!=oldX) {
this.x=x;
propertyChange(Point.x, oldX, x);
}
}
Property Litteral and switch
Property litteral like enums can be used in a
switch. This features is easy to implement if
bug 5029289 (switch on strings) is implemented.
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 as the variable property is typed
Property<? extends Bean, T>
the properties Bean.name and
Bean.value exist.
The class Property
Just a preview of the methods of the class Property.
public class Property<B, T> {
public Class<B> getDeclaringClass() {}
public Class<T> getType() {}
public String getName() {}
public boolean isReadable() {}
public boolean isWritable() {}
public T get(Object bean) {}
public void set(Object bean, Object value) {}
}
public class Class<T> {
public Property<T, ?>[] getProperties() {}
public Property<T, ?> getProperty(String name) {}
}
The signature of getProperties() is illegal
with the current JLS but i hope it will be legal
with the next JLS.
I will implement this spec functions of your comment, cheers
Rémi
Bookmark blog post: del.icio.us Digg DZone Furl Reddit
Comments
Comments are listed in date ascending order (oldest first) | Post Comment
-
Sweet. This proposal keeps getting better. I've got some minor gripes regarding property literals, but for the most part it's fantastic.
Your proposed syntax for accessing the Property literal doesn't work because it uses the exact same name as the variable itself, Bean.name and makes unnecessary use of reflection at runtime.
I would prefer for the Property objects to be 'compiled' into static variables on the classes they are defined in, ie. compiled into this:
class Foo {
private property String bar;
}is syntactic sugar for:class Foo {
private static Property{Foo,String} $PROPERTY_bar = new Property{Foo,String}() {
public String get(Foo foo) { return foo.bar; }
public String set(Foo foo, String bar) { foo.bar = bar; }
}
}I think aspects of properties, such as firing events and validation should be pluggable. The syntax you've proposed for bound properties is not particularly flexible. I've described one possible approach for extensible properties, although I admit annotations might not be the right way to declare them.
Posted by: jessewilson on January 23, 2007 at 08:51 AM
-
whoops! ...corrections to my post. The second class Foo needs to retain the variable bar:class Foo {
private String bar;
private static Property{Foo,String} $PROPERTY_bar = ...
}
}To reference the property literal bar, the syntax could be Foo#bar or something else.
Posted by: jessewilson on January 23, 2007 at 08:56 AM
-
You've neglected the use site convenience aspect - using an operator for property access.
bean.name="Rémi"
Grabbing a property as a literal would by far be used less often than actually putting values into the bean and getting them out. You're making the wrong thing convenient.
What you suggest can already be done using statics in Java anyway, with reflection in the Property class.
public static final Property<String> name=new Property<String>();
Then at call sites - Bean.name.set(bean,"Rémi");
My suggestion is to make properties generate get/set methods, and to allow the dot operator as a convenient way of accessing those methods.
Posted by: ricky_clarkson on January 23, 2007 at 09:06 AM
-
Regarding the Property Literal, I think It shouldn't use the "dot" syntax to read it.
It make a lot of confusion about the "dot" is pointing to the public field or the literal.
Do u have any plan to write a prototype on ur new proposal?
Also the syntax below is valid or not.?
I want a default getter and customized setting
class Foo {
public property String name6 get set(String name) { .....}
}
thx.
Posted by: fcmmok on January 23, 2007 at 10:36 AM
-
Nice idea with set and get modifiers. I think it is enough to have these two modifiers and nothing more to resolve the main problem: dummy setters and getters in most cases. So it would be like this:
class Foo {
private String name0; // variable as before
private String name1 set; // variable as before, public method setName1 is created
private String name2 get; // variable as before, public method getName2 is created
private String name3 set get; // variable as before, both setter and getter are created
}
In all the cases it would simply create ordinary public setter/getters for appropriate variables.
You want to create your own setter for name1? Just remove set keyword from its declaration and create method setName1(String name).
It is simple to learn and does not cause problems with older code.
Posted by: hauser on January 23, 2007 at 12:09 PM
-
Remi, nice wrapup and proposal. Maybe Property class should extend (or wrap) PropertyDescriptor, or maybe not?
Posted by: evanx on January 24, 2007 at 01:30 AM
-
Jesse, the current spec doesn't says that neither the generated field
nor the name of the generated property litteral
has to be named with the same name that the name of the property.
Property.get()/set() must call their corresponding getter/setter
to be consistent with user-defined properties.
I have rejected your approach in conscience because
pluggable litteral doesn't play well with user defined getter
and setter
to be usefull, your pluggable property needs to know the
instance of the bean. The memory consumption of such
design is too high to be reasonable.
Ricky, my thought about such operator, it can't be 'dot' because it
will cause confusion with field accessor.
It can't be another symbol that dot because it will be too weird.
So i propose to NOT define such operator.
Property litteral is important, the bean spec is all about that.
@fcmok, a property is not a field, but i agree that it is not
obvious. Yes i've planned to implement that spec when my
ISP will fix my internet access at home :(
Evan, i would prefer to see a constructor of PropertyDescriptor
that takes a Property as argument.
Posted by: forax on January 24, 2007 at 04:00 AM
-
I'm sorry but inventing another way of writing methods like get {...} and set {...} is pointless. If you want syntactic sugar for automatically generating those, fine, but why on earth would you not just implement the getter and setter the old-fashioned way if you wanted to supply your own implementation?
Posted by: pcholakov on January 24, 2007 at 04:28 AM
-
Firstly, I think you are correct to descope "Access to a property". This is a separate problem area with multiple possible solutions. However, IMO your property proposal should not block a separate property access proposal by using the dot for property literals.
As such, I believe that a different operator is needed for property literals. I think this also makes it more readable, as it highlights to existing Java coders that 'something unusual' is going on. I propose the arrow (pointer):
Person->surname; // accesses the Property
person->surname; // accesses the BoundProperty
Person->validate(); // accesses the Operation (Method)
person->validate(); // accesses the BoundOperation (Method)
I also still prefer Jesse's approach of decorating property implementations rather than a single 'bound' keyword. It is much more powerful and re-uses much more code. You have a memory concern, and so do I. Having implemented something similar, it was fine on the client side - it only really matters on the server-side. So, I'm sure that there are good possibilities there.
The enum is nice though ;-)
Posted by: scolebourne on January 24, 2007 at 05:18 AM
-
Please stop polluting Java with superfluous features and focus on *real issues* like extending the API to cover missing functionality (file permissions, BlueTooth, USB, Data Binding, ...).
Extending the API is the way forward to create killer apps in Java, but adding syntactic sugar is no more than "me too". It won't win a dime...
Posted by: christian_schlichtherle on January 24, 2007 at 06:39 AM
-
First of all I am against having property syntax in Java. We can use the resources in more important tasks. But, since we are discussing properties anyway, I have a problem with the Bound Property. Your proposal is that we declare: propertyChange(Property prop, T oldValue, T newValue) to trap a property change. But my issue is that inside this method I would have to use switch/if-else statements to distinguish between different properties with the same type. I would rather prefer to have different methods for different properties, so I suggest that we use propertyChangeX(Property prop, T oldValue, T newValue) (where X is the property name from example Point.x) to trap a property change.
Posted by: vhi on January 24, 2007 at 07:08 AM
-
@pcholakov, grouping the getter/setter allows the compiler to verify
that getter/setter exists and the developer to easily locate them.
Stephen, i agree that using dot for property litteral is not a smart idea
but i haven't find another syntax.
About the memory issue on client side, binding the properties
of a list of Files to a JTable will create a property object for each
cells of the table ?
@christian_schlichtherle, you want data binding without properties ?
@vhi, why do you want that ?
Posted by: forax on January 24, 2007 at 07:37 AM
-
>> why do you want that ?
It is hard to organise code otherwise, as most of the time we would end up having switch/if-else statements similar to:
switch(property) {
case name:
propertyChangeName(property, oldValue, newValue);
break;
case value:
propertyChangeValue(property, oldValue, newValue);
break;
}
Otherwise (if we do not delegate to methods), if I want to override a particular 'trapping' of a property change (say "value" property) in a sub-class, it will be difficult.
Posted by: vhi on January 24, 2007 at 08:09 AM
-
Remi, a table of FileBean objects does not have to create a property object for each cell. The column refers to a meta-property, the row to a bean. There is no need to create the bound-property for the cell - the painter can simply query the data using the bean and the meta-property.
Bound properties should be created on demand so they are short-lived Eden GC objects. Using the arrow syntax, person->surname compiles to new BoundProperty(this, "surname");
Posted by: scolebourne on January 24, 2007 at 08:09 AM
-
Hey Remi,
Nice post! In the last couple of weeks I've reached most of the same conclusions. I've just posted another blog (http://weblogs.java.net/blog/rbair/archive/2007/01/remis_property.html) which details a few variations I'd recommend.
Stephen: I know you were proposing Property objects that would have a get()/set() method that implicitly references the Object that the property belongs to, such that foo->bar.get() could return the value of the property "bar". However, I tend to agree with Remi that the Property literal object should really be on the same level as Field and Method. But I haven't quite made my mind up on this point.
Posted by: rbair on January 24, 2007 at 09:43 AM
-
This suggestion is getting really complex.
First off notice that in name5 a field will probably exist that is never used, seems like not such a great direction? Why should a property allow a caller to return a value other than the value of the field?
I can see the logic of preventing the setter from setting a bad value and there is a use case for overriding the getter but why return a different value from the field?
The only use case I can see for something like that is when we want a property to reflect something that is not a field, this need isn't addressed by any property proposal including this one. So I personally think that at least the return type should be implicit for the getter.
Then there is the matter that add/remove property change and the property change support isn't streamlined with this suggestion. So a major block of code will still need to be copied and pasted for client developers.
Third thing is the propertyChange method callback won't allow a generic implementation such as the one here, when indexed properties are involved:
void propertyChange(...) {
propertyChangeSupport.firePropertyChange(p.getName(), oldValue, newValue);
}
There are possibly other complications related to such a method, but I haven't given it too much thought. I don't see how properties can be implemented in a generic way for both the client and the server platforms, they just have radically different use cases for beans.
Posted by: vprise on January 24, 2007 at 10:27 AM
-
I am strongly against the inclusion of properties at the language-level. In my view they are utterly useless (rarely used for real APIs), redundant (IDEs could generate them on the fly) and increase the learning curve of Java.
If you're using a GUI editor or Hibernate-like persistence that requires JavaBeans, the problem is with *those* frameworks, not with the language of Java. It is horribly bad practice to expose all your fields this way as properties. It leads to horrible API which Java as a language should *discourage*. It is actually a *good* thing that bad APIs are harder to write using Java. Just my 2 cents :)
Gili
Posted by: cowwoc on January 24, 2007 at 12:58 PM
-
This point has probably been made before countless times, but what if I want to turn a property with straight getter/setter into something that is calculated.
For example, I have an Employee class with firstName and lastName properties. Then we introduce a PersonName class which itself has first and last names and want to change Employee to use this new class (because it has extra functionality for formatting names, etc.).
So before the change Employee looks something like:
class Employee
{
public property String firstName;
public property String lastName;
}
and after it becomes:
class Employee
{
//Property or field?
private PersonName name;
public String getFirstName()
{
return(name.getFirstName());
}
//...same with setter and lastName
}
So if we use the get/set() syntax for property names we maintain comptability, e.g. getFirstName() still works.
But what about code that uses the new property literal syntax:
Property firstNameProperty = Employee.firstName;
breaks in this case. Breakage also happens if we use another symbol to access properties directly: Employee#firstName.
So we apparently need additional syntax to support this, or choose not to support this at all which means once a property is defined we are locked into the design - and the whole point of getters/setters was so we could change implementation details in the class (like in this case) without having to change public attributes.
And if an additional syntax is added to support this case it makes it more complex - and the more complex something is the more we should question the need for it.
Personally I'd be happy if things stay the way they are, or have the property keyword just be a shortcut for making getter/setter which we could override just by writing the getter/setters ourselves if we wanted to.
Posted by: prunge on January 24, 2007 at 06:17 PM
-
Here's how pluggable literals work with user-defined getter and setter. This source code:
class Foo {
private property String bar get { return "hello"; } // read-only
}is syntactic sugar for this:class Foo {
private static Property{Foo,String} $PROPERTY_bar = new DefaultProperty{Foo,String}() {
public String get(Foo foo) { return "hello"; }
};
}and how would user-defined extensible properties interact? Here's an idea. This source code:class Foo {
private property(new ObservableProperty(), new NonNullProperty()) String bar;
}is syntactic sugar for:class Foo {
private String bar;
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);
}
}...and here's the property interface:public interface java.lang.Property {
void setDelegate(Property other);
void set(B bean, V value);
V get(B bean);
}
Posted by: jessewilson on January 24, 2007 at 10:47 PM
-
I explain the above syntactic sugar here.
Posted by: jessewilson on January 25, 2007 at 12:23 AM
-
You started this post with a proper question:
Why do we need a property syntax in Java ?
But the answer is simple - nobody really wants it or needs it
You can check with the poll
http://java.net/pub/pq/142
Why don't we focus on really necessary features instead of wasting time for something that gives no real advantages?
Posted by: 4bugzilla on January 25, 2007 at 12:49 AM
-
I have a problem understanding these cases:
class MyBean {
public property String name2 get; // read-only
public property String name3 set; // write-only
public MyBean(String name2value) {
// How would I set the value returned by getName2()
// to the value of name2value ? What Syntax to use?
setName2(name2value); // I expect this to be illegal because
// name2 is declared read only.
}
void someOperation() {
// here I need to know the value of name3 to perform my
// my operation. How do I access it? Again I would
// expect getName3() to be illegal, as it is read-only.
}
}
The above aside, I really really like this proposal, because finally it addresses the problem of bound beans properties, and without this feature I just don't think any proposal is worth the effort.
Just one small enhancement: Especially with swing properties you often have to add a single line of code. Similar if adding constraints. It would be really nice to be able to combine your own code with the generated code, for example like this:
class MyOtherBean extends SomeSwingComponent {
public property String key
get // tells compiler to generate the default getter
set(String k) {
if (k==null) {
throw new IllegalArgumentException();
}
super.set(k); // calls the generated code
// (actually, the compiler could just replace this
// call to super with the generated setter code.)
}
public property Color customColor bound
get
set(Color c) {
super.set(c);
repaint();
}
public property HugeObject lazy
get {
HugeObject rv = super.get();
if (rv == null) {
// oops, we did not yet create the huge object,
// so do it now:
rv = new HugeObject();
setLazy(rv);
}
return rv;
}
set; // tells the compiler to generate the default setter.
}
And some final questions you should consider in the next version of you proposal:
How to override getter and/or setter in subclasses? (It's straightforward in your proposal, but for example not so straightforward if you adopt Richard Blair's proposal to get rid of the actual getXxx and setXxx methods.)
How to handle name collisions in subclasses? (especially the case property hides property, where Superclass.prop.set(bean,v) sets an other value than Subclass.prop.set(bean,v) and bean.setProp(v).)
What's the syntax for indexed properties? Please do not forget them! I'd propose something straightforward like "public property String[] name;" which would then produce "String getName(int)" and "void setName(int,String)" in addition to the "String[] getName()" and "void setName(String[])" methods you would generate now. And "bean.name" should return an instance of IndexedProperty with appropriate indexed method signatures.
Posted by: rullrich on January 25, 2007 at 04:23 AM
-
I'd like to state that I'm someone (not "no one") who would really like to see property access in the java language.
I don't like having to use the get/set methods to access the properties - it kills half the point of having properties - simpler acess. object.name is just so much cleaner than object.getName(), and object.name = "Tom" is so much cleaner and consistent with how variable values are set than object.setName("Tom").
Yes, this leaves you with 2 ways to access the same value, but without it you're left with 2 different and incompatible ways to get/set a value - sometimes you use .value or value =, and sometimes you use getValue() or setValue(), depending on whether you're using a property or a local variable. Oh, but if you're using jsp's (which seems to be the #1 biggest use of java), the expression language accesses properties through "object.value". So you're left with "If it's a local variable, use ".". If it's a property, use the get/set method. Oh, but if it's property from a jsp page, use "." again...
I also wanted to bring up another minor point I thought of - wouldn't it make sense to make a "property" public by default? The public part of "public property String name1" could be left off. You could still put "private" there, if you wanted, but public would be the default.
Posted by: paulrivers on January 25, 2007 at 09:48 PM
-
I'd also like to mention, like a bazillion other people, that the idiots who wrote the java.net blog software, which doesn't automatically add in linebreaks, are idiots.
Posted by: paulrivers on January 25, 2007 at 09:50 PM
-
@paulrivers. You are insulting the idiots!! ;)
Posted by: mikaelgrev on January 26, 2007 at 01:46 AM
-
Gili, i don't write code for tools but for the fellow developers
that need to read it or worse debug it.
@4bugzilla, it depends how you read that poll. I see
6% of the developer saying that property syntax
is a must have.
@rullrich, i fuck up, i've totally forget that constructors can't
use getter and setter.
Paul, another idea is to add a warning for getX/setX if the compiler
target is greater or equals than 1.7 saying that a future version will
dissallow that.
Posted by: forax on January 26, 2007 at 02:58 AM
-
Hi,
I think that adding keywords just to handle properties is not the way I would do it.
I'd rather use annotations:
class XYZ {
@Property() // uses the default: get, set and bound
private String name = null;
@Property(get=true, set=true, bound=false)
private double balance = Double.NaN;
@Property(get=true, set=false)
private String uuid = null;
}
That's it.
No new keyword. Just a new annotation. The compiler does the rest.
Posted by: janaudy on January 29, 2007 at 05:04 AM
|