 |
Remi's Property Proposal
Posted by rbair on January 24, 2007 at 09:39 AM | Comments (27)
This is going to be a short one. To begin, be sure to read Remi Forax's blog about his refined property proposal. Read it? Good.
I think Remi is really on the right track, and I can't wait to get the next build of his javac compiler to play with it. In the week or so since I last blogged on properties, I've had a chance to consider and reconsider a bunch of alternatives. I came up with the same basic set of criteria that Remi has. But there are a couple things in his proposal that I'd tweak.
In my last entry, I made the case for needing the property feature to support auto-generating getters/setters so that current frameworks that rely on the JavaBeans spec continue to work with the new properties. Frameworks such as JSR 295 (beans binding), Spring, JPA, etc. jhook pointed out that we actually don't need to do this: as long as the java.beans.Introspector can read the new Property information in the class file, then frameworks would continue to work perfectly, without the need of introducing auto-generated getters/setters.
After thinking about it more, I am not in favor of auto generated getters and setters. It just feels wrong. You can read the source file and there will be no method "setFoo". Yet in another source file, there you are, calling "setFoo"! Called me old fashioned, call me over the hill. It just doesn't sit right.
Instead, I'm of the opinion that if properties are introduced in the language, they should be a clean implementation. Let the Introspector bridge the gap. And stick with Java's core language tenet: keep it readable!
This is why I really like Remi's proposal (which Joe Nuxoll also proposed at one point):
public property String foo; #read/write property
public property String bar get; #readonly property
public property String baz set; #writeonly property
public property String wow get() {return bar;} set(String w) {bar = w;} #property with code
He then goes on to talk about how to access a properties value. Here I part ways with Remi for a bit. I still think the "." accessor is the best choice. Thus:
obj.foo = "Hello";
String s = obj.foo;
One argument that has been made against the use of the "." notation is that it may have unintended side effects for the person making the call. Yes, it may. But then again, obj.setFoo("Hello") may have unintended side effects. If properties receive special attention in the javadoc (as I'm sure they would), then I see the unintended-side-effect argument to be much weaker. Want to know if there is a side effect? Read the docs! When you read the source code or docs for the class that declares the property, it is very clear what is going on.
Remi, Stephen Colebourne, Evan, and others have talked about a Property Literals (aka Property References, etc) which I think is a really, really great thing to be thinking about. Here is my minor tweak to Remi's proposal: use "#" as the operator instead of the ".". We already use # in the javadocs, so it would be a natural and consistent use. For example:
class Person {
public property surname;
public property givenName;
/**
* @return the #surname and #givenName, formatted
*/
public String formatName(String msgFormatPattern) { ... }
}
...
String s = person.surname; //yields the value of the surname property
Property p = person#surname; //returns the Property object for the surname property
p = Person#surname; //also returns the Property object for the surname property
Method m = Person#formatName(String); //holy toledo! Returns a Method object!
As you can see, there are some benefits to introducing a new operator for this task. # to me, makes a lot of sense.
That's it for now. I hope to be able to chew on this problem a bit more.
Bookmark blog post: del.icio.us Digg DZone Furl Reddit
Comments
Comments are listed in date ascending order (oldest first) | Post Comment
-
Why should person#surname and Person#surname return the same thing? It doesn't make sense. The former should return the bound-property (with bean), the latter the meta-property (without bean). Since the consensus is to call the latter 'Property', then we should call the former BoundProperty.
The same applies to methods. Except that I think that it should return Operation/BoundOperation, which is an interface that is implemented by or delegates to Method. (This is much better for integrating more dynamic behaviour - imagine a Map-like structure with dynamic 'properties' and 'operations')
Finally, I suggested ->, as I used # for something else.
Posted by: scolebourne on January 24, 2007 at 09:54 AM
-
About auto generated getters and setters: We already have generated constructors that we explicitly call, with implicit super() calls in them. As long as the rules for what gets put in the getters and setters are consistent, as I don't see a problem with it.
Having the generated getters and setters makes sense if you want your new beans to be able to work with old bean-using code. Relying on the introspector filling in the gaps might not always work, if some existing code introspects with reflection.
I agree with you about using the . operator for property access, and # for property literals.
Posted by: ricky_clarkson on January 24, 2007 at 11:07 AM
-
What's wrong with generated code? It wouldn't be generated per-se, only as a syntax tree addition in javac...
Also, note that if there isn't a way to add a single line of "custom" code to the setter, Swing properties will be severely crippled, probably to unusable. Often (mostly?) you need to call repaint(), revalidate() or some code that invalidated the internal state when a property is changed. Otherwise the visuals won't be updated! The user of the bean can't be given the burden to revalidate the bean for every change. And adding a listener to your own object for this would not only cripple performance but put that code out-of-context (yes, leads to bugs).
I hope this properties feature won't be crippled because of "feelings" and personal coding style lock-ins. If real developer surveys and proper use-case analysis shows that, for instance, the custom code thing isn't viable, by all means drop it, I won't disagree then. Otherwise, solve the problem! ;-) ;-) (Please notice the double-smiley!)
Cheers,
Mikael Grev
Posted by: mikaelgrev on January 24, 2007 at 11:41 AM
-
@scolebourne: Er, calling it a BoundProperty sounds like it fires property change events, not like it is "bound" to a particular bean instance, yes? "Bound" as I'm sure you are aware is a very well-defined concept in Java Beans.
Posted by: ljnelson on January 24, 2007 at 12:15 PM
-
Hey Mikael,
I don't understand your point? The "wow" property in the first code sample shows how to use custom code for the accessor and mutator.
What's wrong with generated code? Simple:
class Foo {
public property String bar;
public property String baz get {
return bar.toUpperCase();
}
}
foo.bar = "ok";
String s = foo.baz;
The Foo class is quite easy to read and to know what is going on. Unlike:
class Foo {
public property String bar;
public property String baz;
public String getBaz() {
return bar.toUpperCase();
}
}
foo.setBar("ok");
String s = foo.getBaz();
Autogenerated methods, in my opinion, unnecessarily complicate the language. In this simple example (which you can imagine would be quite a bit more complicated in the real world) there are 3 odd gotchas. First, to change the behavior of the accessor of "baz", I have to write a method called getBaz(). For longtime javabean/java guys it isn't that big of a mystery, but it IS odd. Second, I didn't write a setBar(String) method anywhere, yet there it is! Third, there is no "setBaz(String)" method declared. Does this mean that there IS no setter, or does it mean that I simply haven't overridden the setter?
I hope this makes clear the disadvantages. The only way to know what is going on, when methods are autogenerated, is to know what is going on. You can't simply read the code.
Posted by: rbair on January 24, 2007 at 01:22 PM
-
There's absolutely no need for get and set keywords to identify read-only and write-only properties. Instead what you need is this:
public property String bar; #readonly property
private void setBar(String s) {}
public property String foo; #writeonly property
private String getFoo() {}
Posted by: elharo on January 24, 2007 at 01:27 PM
-
@ljnelson: There's an overload of the term "bound" here for sure. Java would need yet another case of its own word, but scolebourne's concept would still be nice to support.
Posted by: tompalmer on January 24, 2007 at 01:30 PM
-
@elharo: How is that an improvement? get and set wouldn't be new keywords, but rather location-sensitive. That is, if you tried to put anything after the variable identifier now, you'd get a compile exception. So we can easily add "keywords" following the identifier without breaking existing code. Likewise, these "keywords" won't conflict with the use of "get" or "set" anywhere else in the application. Heck, you could even write:
class Foo {
public property String get get { return "Hi!"; }
}
...
Foo f = new Foo();
String s = f.get;
Posted by: rbair on January 24, 2007 at 01:31 PM
-
I see the confusion in my reply re bound-property. What I mean with this terminology is an object that contains the meta-property information (property name and type) and a reference to an instance of the bean itself. How about calling it linked-property instead?
Posted by: scolebourne on January 24, 2007 at 03:39 PM
-
@scolebourne: Seems to me we already have the meta-property information in PropertyDescriptor; I kind of like that term, but I tend to be in the minority. Anyhow, if the metadata is a PropertyDescriptor, then the Thing Itself would be, quite simply, a Property.
Posted by: ljnelson on January 24, 2007 at 05:31 PM
-
Method m = Person#formatName(String); //holy toledo! Returns a Method object!this is very sexy.
I really agree with what you're saying here, Richard. I've refined my ideas for extensible properties to tidy up the syntax a bit and add an example of what the syntactic sugar would resolve to in the parse tree.
Posted by: jessewilson on January 24, 2007 at 11:58 PM
-
@richard, As always it's hard do make oneself understood with short messages. Especially since the context is different all the time. I think that by themselves simple getters and setters aren't worth the language change since every IDE can autogenerate them and you never have to see them if you put them at the bottom. When properties starts to get interesting is when you can have for instance bound properties with just a simple bound (with equality checks) annotation or some other syntax that is simple and resonably short. This is where the the "custom code" statement comes in. If you can have simple get/setters and bound properties automatically handled but you can't inject code when for instance a the bound property changes the whole point of properties is clippled a lot. Normally in a POJO or Swing component you need to do something when the bound property changes, such as repaint. When you say that you can have custom code you mean (as I understand it) that you can choose to write the whole method manually. But then there is no point in having the improved syntax since you will have to write the boiler plate for the bound firering and all, probably for all setters in a component... This is what I mean, I hope I explained it more clearly now. (This web client doesn't really lend itself to long explanations though..) If you still are in doubt about what I mean re-read my post at Javalobby but disregard the syntax I used. I'm fine with any syntax as long as the use-cases are met.
Also regarding the secon thing, about whether getters and setters should actually be generated I think we aren't that far appart. I think the getters and setters should be generated only for backward compatibility. You should still access the property with the new syntax (bean.foo or bean#foo or bean->foo or whatever). I just think that if you really want to, or have old code that uses it, you can still call getFoo() and setFoo(..) on a property and the compilation won't fail. I agree that calling getFoo() when the property hasn't defined a getFoo() isn't perfect but the developer can choose for new code and should be using the new syntax. The getter and setter has to be generated by javac in some way anyway and I think that it can just expose the get/set method names as there are no apparent downsides except for them who chooses to mix new property definitions with old-style getters and setters. The price is that code can look a bit ugly if coded in mix but the gain is that old tools understand classes created with the new syntax without having to be re-coded.
Cheers,
Mikael Grev
Posted by: mikaelgrev on January 25, 2007 at 12:54 AM
-
NOOOOO.
Please stop changing Java in funny ways.
I already have to shudder every time I see @Override.
Please stop to clone every single C# feature. If I want to use C# I'll use it.
Posted by: pjmlp on January 25, 2007 at 01:47 AM
-
Richard, lovely stuff, thanks to Remi and yourself for these great efforts and thoughts. I like the hash notation you propose, and also getting at methods as well as properties.
to the detractors of "changing java," in case you haven't noticed we're in stiff competition with C# ;) and so surely we should look at great features of C# et al, which can make java even better, and more attractive to new developers, as was done with Java5? Consider C# developers that are put onto java projects and visa versa. I reckon having C# and Java as similar sibling languages is good for developers? Also for writers eg. of patterns, algorithms, text books, blogs and whatever - an example might be presented in java or C# and be clearly understandable to all C# and java developers, and readily applicable.
With java being opensourced, there's no stopping such proposals being implemented as a research exercise eg. in the KitchenSink, and people can try them out, and then there can be meaningful debate supported by real examples of usefulness or not, and if warranted, a JSR. Why should we want to discourage such opensource R&D?
Posted by: evanx on January 25, 2007 at 03:02 AM
-
Richard, i'm with you on not generating setter and getter bytecode at compile time eg. supporting setFoo() when no such method exists in the code - as you suggest, as long as java.beans.Introspector supports these new "language properties" we should be fine :)
Posted by: evanx on January 25, 2007 at 03:08 AM
-
Richard, we need auto-generated getter/setter in that scenario.
I have already written a class like that :
class Bean {
public property String name;
}
Now, i want to change its implementation to somethink like that :
class Bean {
private String firstname;
private String lastname;
public property String name
get { return firstname + ' '+ lastname; }
set(String name) {
String[] names = name.split(" ");
firstname = names[0];
lastname = names[1];
}
}
If i do want to break the binary compatibility,
i need auto-generated getter/setter, i.e. any access to
a property must use getter/setter.
Rémi
Posted by: forax on January 25, 2007 at 03:27 AM
-
The moment Java becomes such a schizophrenic language I'll be developing in Ruby/Python, or even in C#. Why play catch up with C# if I can use the real thing?
The 1.5 changes were mostly ok, but for these 1.7, I'm really afraid of what Java will turnout to be.
Posted by: pjmlp on January 25, 2007 at 04:10 AM
-
The code will get extremely messy really quickly if you require the code for the getters and setters to be placed like Remy proposes (except in trivial cases like you demonstrate).
If the getter or setter are longer than a few short words you will need a lot of code in your variable declarations section, just when we've gotten most people used to the idea of separating out variable declaration and method code for clarity.
Posted by: jwenting on January 25, 2007 at 04:29 AM
-
"so surely we should look at great features of C# et al,'
Ya, we should look for good features that enhance the productivity of developers. GC, Interface, for-each are good feature that can help the developers to write better code :) Imagine the time without GC in C/C++, 'delete", "new" are an insane.
C# learned a lot of features from Java, so why not we get back something from it ?
Posted by: fcmmok on January 25, 2007 at 05:08 AM
-
I used to hate Java Language Extensions as much as the next guy. But since I started using 5.0, I have realized that advancements can be good. And this is starting to look like a good idea.
Posted by: pcwheels on January 25, 2007 at 06:21 AM
-
@jcmmok nothing wrong with that in principle, but the trend is to think that anything that's in any other language is by definition good to add to Java.As a result Java is threatened to become a messy grabbag of "features" added for no other reason than that people think they look "kewl". I'm in dubio about properties. While they have a place in some niche areas (like GUI designers), I don't think retrofitting them into a language is a good idea.While the creators of Delphi did a pretty good job at it (showing it can be done) the use of properties in Delphi is rather limited, essentially they are there exclusively for use by component developers.
Now it must be said that I'm currently a component developer in Java, but those components are mainly EJBs and SOAP services where the external interface of the component is made up mainly of service methods rather than fields (with or without getters and setters). While there are DAOs and DTOs in there, their number is limited. A properties system would save my users a few keystrokes, but cost me a lot of effort to create the code for them, as they're more verbose than the getters (and sometimes setters) we have now, especially for the more complex classes employing numerous inner interfaces.
I also wonder how you're going to define a property through an interface. If I've a class containing a property someProp, a class that implements an interface SomeInterface which is the only public API, how would I make that property public through that interface? All proposals I see are centered purely on class level implementation of the darn things, and don't take that into account (at least they've now gotten around to adding read-only properties, though I'd want to see more and have (for example) a protected setter with a public getter).
interface SomeInterface {
property String someProp get;
}
Looks to be about the most logical thing to add at interface level for a readonly property.
At class level it could then be redefined as get/set to allow a published getter with a protected setter maybe.
Posted by: jwenting on January 25, 2007 at 07:09 AM
-
Remi, that's an implementation detail. You don't have to autogenerate setName() and getName(), but could instead generate MyClass$setName() and MyClass$getName(), or something along those lines, similar to how inner classes are handled. My point is that generating the getName() setName() methods is messy, imo.
Posted by: rbair on January 25, 2007 at 09:53 AM
-
@jwenting:
You bring up some really good points that I don't think have been discussed yet. Just a minor nit or two:
the trend is to think that anything that's in any other language is by definition good to add to Java.
I know that I don't subscribe to that theory, and I doubt any other influential people in the language do either. What you are probably observing is that everybody has their pet language feature, and if you get enough people with enough pet features into one room you are bound to have enumerated every feature imaginable. I don't think Gosling, Gafter, etc are proposing language features simply because C#/Ruby/Lisp has it.
at least they've now gotten around to adding read-only properties
All the proposals I've seen (dating all the way back to Joe Nuxoll's proposal back in 2004/2005) have had read-only properties :-)
Ok, those were my only nits. On to the points.
I also wonder how you're going to define a property through an interface
Good question! I haven't put one lick of thought into it yet. I'll have to stew on that one.
A properties system would save my users a few keystrokes, but cost me a lot of effort to create the code for them, as they're more verbose than the getters (and sometimes setters) we have now, especially for the more complex classes employing numerous inner interfaces.
I find this interesting. Do you have an example? It seems like it can only be more verbose if you have many getters without a corrosponding field. How dow inner classes play into this?
Thanks!
Posted by: rbair on January 25, 2007 at 10:03 AM
-
@mikael: I see what you mean. This is somewhat related to the Property and MetaProperty proposals floating around, and related to the "meta" stuff that Groovy uses. This requires more thought.
But I disagree with the autogenerating of the getFoo()/setFoo() methods. Legacy code should be using the property descriptor, not calling the methods directly. The Introspector would be updated to create PropertyDescriptors based on the new properties as well. So any code that was written to spec (ie: used Introspector and/or PropertyDescriptor) would continue to work flawlessly. At least, that's the theory :-)
Posted by: rbair on January 25, 2007 at 10:29 AM
-
@richard, If you can pull that off then I'm OK with it. I thought that many tools were actually looking for the getXxx() method signature but if they mostly are using the Introspector we should be fine. :)
Posted by: mikaelgrev on January 25, 2007 at 12:09 PM
-
Richard, just a smallish example.
public interface IntervalDataInt {
long getTickerId();
long getSourceId();
Date getStartDate();
long getDurationId();
TickIntervalDataInt getLast();
TickIntervalDataInt getBid();
TickIntervalDataInt getAsk();
public interface TickIntervalDataInt {
PriceDataInt getOpen();
Date getOpenDate();
PriceDataInt getClose();
Date getCloseDate();
PriceDataInt getHigh();
Date getHighDate();
PriceDataInt getLow();
Date getLowDate();
Long getVolume();
}
}
PriceDataInt itself also is an interface that employs inner interfaces.
So you would have a property that's of an inner interface type which itself has properties of interface types that have their own inner properties of inner interface types.
Everything in the implementation classes is readonly, all the data being set through the constructors (they're return values from stateless EJBs in a system where the consumer has no capability of inserting data).
The code to write the property definition here would likely be more complex than the code needed to write the getters, especially since it would be essentially duplicated in the interface as well.In a situation where there are more complex setters (and maybe getters) involved it gets even worse, with the code in the class definition (rather than the interface) cluttering the member definitions.To make the code cleaner some way of forward declaration of the getters and setters in the class level property definition would be needed, so you can define all the properties at the top of the class and follow them with all their getter/setter definitions at leasure
Something like the following might work (which is somewhat like Delphi does it):
public class SomeClass {
property String theString: get=getTheString() set=setTheString(String s);
// ....... lots of other code
public void setTheString(String s) {
theString = s;
}
public String getTheString() {
return theString;
}
}
Posted by: jwenting on January 25, 2007 at 10:40 PM
-
And why not a macro preprocessor instead of adding new core keywords (like "property") every time we "all" need to generate repetitive code?
Posted by: nicarran on March 18, 2007 at 09:53 AM
|