Paul Hammant's Blog: At least one unilateral improvement to Java
Intro:
Ruby/Rails etc etc are pummeling Java presently. At least, the ranks of
Java's
intelligentsia are hemorrhaging to a Ruby/Rails world and not coming
back according to their current plan. From James Duncan Davidson
some years ago & swathes of other Apache people since,
to every-loves-to-hate-him-online-but-he's-great-fun-really Obie Fernandez.
Let us not forget Dave
Thomas or Bruce
Tate (both ticked
at Greg Luck's blog
post) dissing the hype surrounding Ruby/Rails. We are currently witnessing the closeout of Java 6's feature set and delivery looming FCS, and there little in it to react to Ruby or Python's terseness. Now, Java can't quickly become a dynamic language and maybe should not for its core, but perhaps a pragmatic goal is attainable: host dynamic languages more effectively. The likelihood is that people will code applications that are hybrid dynamic and static. Consider a Hibernate (or iBatis or http://esper.codehaus.org/) Java tier used by the 4GL-esqe Rails via JRuby (still a 3GL) all in a single JVM. Java is lacking some features that facilitate a dynamic excellence, despite being rooted in a traditional static Java. It all centers around methods and method access I think. Some will claim I'm stretching some points, but the success of integration with scripting languages or the robustness of Rails-like libraries for Java rests on the simplicity and flexibility of reflection in Java.
I propose two things in the remainder of this blog entry. Making method access (almost) part of the language, and Granting access to methods by their parameter names before Sun gets round to it. Read on....
1) Making Method access part of the language
Presently you have to:Method doFooMethod = Foo.class.getMethod("doFoo" new Class[] {Param0.class, Param1.class, ParamN.class});or for Java 5.x ..
Method doFooMethod = Foo.class.getMethod("doFoo", Param0.class, Param1.class, ParamN.class);
What I'd prefer is..
Method dooFooMethod = Foo.class.methods.doFoo(Param0.class, Param1.class, ParamN.class);or
Method dooFooMethod = Foo.methods.doFoo(Param0.class, Param1.class, ParamN.class);The above syntax will allow Jetbrain's IDEA (or Eclipse / Netbeans etc) to allow easier and safer refactoring of methods. At least if a codebase is spattered with Reflection usage. For God's sake. if this is ever implemented, PLEASE give it unchecked exceptions.
Because that is years off, I'd like something else in the meantime:
If JetBrains and Eclipse people to get together and agree a kludge. I don't think the NetBeans people would play, for now. No matter.The kludge would be that the screen representation of method access in that style as I propose above. But behind the scenes in the actual source files there is a different annotation:
Method dooFooMethod = SomeHelper.getMethod(Foo.class, "doFoo", Param0.class, Param1.class, ParamN.class);The helper class would have to be in the codebase of the project somewhere as it makes no sense for the five lines of Java to be in an external jar that all would have to depend on. The helper class would also be pretty standard, and marked in some way so that IDEA/Eclipse know its that hook point for the kludge. It is also the case that there may be situations where a mismatch can't be detected until runtime:
Method dooFooMethod = bar.getClass().methods.doFoo(Param0.class, Param1.class, ParamN.class);Of course the last case is a stretch, many would argue that traditional reflection-like access would be best. One might be trying to be consistent though. There is possibly some fine-tuning there that centered around casting concepts that aids the refactoring IDE into knowing what's going on.
Method dooFooMethod = bar.getClass().methods.doFoo(Param0.class, Param1.class, ParamN.class).asIn(FooInterface.class);
2) By-parameter-name method access in Java
I've blogged before on parmeter names in Java here and here. More recently I pushed a new project to Codehaus. ParaNamer uses QDox to extract parameter names from Java source and add them to a text file in META-INF of the jar you're about to build. QDox barfs on annotations so use with Java 5 with measure. Clearly Joe Walnes needs escape the clutches of Google for 5 mins or so to fix up QDox so that it works with Java thru 6.0. ThoughtWorks had a kidnap squad assembled at both OSCON and Agile2006, but Joe was no-show to both. I plan to maybe leverage codavaj and ASM to allow second and third ways of extracting parameter name info from methods and constructors. I'm told the Spring team have some ASM know-how to extract parameter names from debug tables, so an email to Rob Johnson and Juergen Hoeller is looming.Whether generated or extracted, the parameter names data goes shoved into a txt file as mentioned. Some may say that the txt file could be better as some modified byte-code design, but that would make for amore complex client.
As it is the client usage is pretty simple, and I'm encouraging a cut/paste solution rather than dependence on yet another jar.
If you have not clicked thru to ParaNamer's single web page yet, here is how it works on usage :
Method method = new Paranamer().lookupMethod(AnyClassInTheJarInQuestion.class.getClassLoader(),
"my.ShoppingCartAction", "addToCart", "make,model,qty");
Or better still (fictional until Sun implement it):Method method = ShoppingCartAction.class.getMethodForParameterNames("addToCart", "make,model,qty");
or either of
Method method = ShoppingCartAction.class.getMethodForParameterNames(ShoppingCartAction.addToCart, "make,model,qty");
Method method = ShoppingCartAction.class.getMethodForParameterNames(ShoppingCartAction.methods.addToCart, "make,model,qty");
Why is this all needed you ask?
Well both for JRuby and Groovy, there is a very declarative syntax that allows the 'composing' applications or at or marking up of data that's very readable. Its very pleasant to those making it and very accessible to the people wearing the hat of deployer rather than coder who may want to fine tune some of the configuration or assembly of an application without recompiling it. Read the Groovy Markup page as to why this is great or any decent article on closures for Ruby etc.Here's a quick example in Groovy Markup syntax that's slightly reminiscent of the stuff we did in NanoContainer:
container {What happens behind the scenes is a NodeBuild implementation does these steps in order :
component(class:ComponentOne)
component(class:ComponentTwo)
}
this.someContainer.addComponent(ComponentOne);Now, existing Groovy coders have to write a NodeBuilder (this is actually a recent simplication to hide a complicated underpinning API). The NodeBuilder looks like so:
this.someContainer.addComponent(ComponentTwo);
public class ContainerNodeBuilder extends NodeBuilder {But if we have easy access to parameter names in Java, it would be easy to have a NodeBuild bundled with Groovy that could adapt almost arbitrary objects to builder syntax. It might prepend "add" to the apparent method name to try to map 'component' from the markup to 'addComponent' on the methods on Container.class, but Rails teaches us that there a lot of mileage in conventions.
private final Container container;
public ContainerNodeBuilder(Container container) {
this.container = container;
}
protected Object createNode(Object name, Map map) {
if (name.equals("component")) {
return container.addComponent((Class)map.remove("class"));
} else {
// other 'methods' , 'elements' or 'nodes' here - depending on your choice of metaphor
}
return null;
}
}
public class DefaultNodeBuilder extends NodeBuilder {
private final Object toOperateOn;
public DefaultNodeBuilder(Object toOperateOn) {
this.toOperateOn = toOperateOn;
}
protected Object createNode(Object name, Map map) {
String methodName = "add" + camelCase(name);
String[] parameterNameChoices = toOperateOn.getClass().lookupParameterNames(methodName);
String bestFit = findBestFit(parameterNameChoices, map) // tis going to use the keys from the map.
Method m = toOperateOn.getClass().getMethodForParameterNames(methodName, bestFit);
m.invoke(toOperateOn, getParameterArrayInOrder(bestfit, map));
}
}
With JRuby it just as simple of course. I've been chatting to the its lead developers about Parameter names and they are warming to it. Charles O Nutter even suggests in email that its critical to get parameter name access for DO-178B certification for real-time Java:
"Just
an interesting little nugget: I was flipping through that ADA vs
Java Real Time presentation linked off Lambda last week and noticed
the authors listed the lack of named parameters as a detriment to
certifying Java code for reliability and determinism constraints in
DO-178B. Perhaps there's a selling point for adding them? :)"
See http://lambda-the-ultimate.org/node/1627Java Real Time presentation linked off Lambda last week and noticed
the authors listed the lack of named parameters as a detriment to
certifying Java code for reliability and determinism constraints in
DO-178B. Perhaps there's a selling point for adding them? :)"
So why not for J2SE as well?
The JRuby as marked up (being coded for NanoContainer by Nick Sieger presently), would look like the following if we ignore the requisite imports:
container {Arguably the JRuby closure syntax is a little less elegant that the Groovy markup.
component(:class => ComponentOne)
component(:class => ComponentTwo)
}
While we are at it I'd love elegant closures in JavaScript (or at least Rhino), Jython and BeanShell. And Java, but that's another story.
So I'm feeling there is a sweet future for Java born from these hybrid applications. A future either scripting a Rails-like presentation tier (with two-stage JITing) for an otherwise class n-tier application, or some scripting bootstrap (or 'master composition' script) at the bottom of the layer cake. We've had both in NanoContainer for some years.
Mauro Talevi (fellow Pico/Nano committer and XStream trustee) made it to OSCON this week, and together we've met many amazing bright people. It has been great. Mauro, on meeting people, can actually chat to them about mutually interesting topics. I'm cursed with a deep need to repeat the same stories over and over to different people we've just met or re-meeting after last year ;-) Mauro, sorry for subjecting you to unending I-hate-XML, general markup and parameter-name related diatribes bro...