Tapestry Training -- From The Source

Let me help you get your team up to speed in Tapestry ... fast. Visit howardlewisship.com for details on training, mentoring and support!

Sunday, March 26, 2006

Static imports and Java language evolution

It's interesting to see the differences between how you expect to use a new language feature and how you end up really using it.

For example, of all the new features in 1.5, the idea of static imports seemed to be the least useful. From the outside, it looks like you get to say "min()" rather than "Math.min()". Big woop.

But what I've found when coding using static imports is a new clarity to my code. Once you strip away the class names from static methods the part that's left starts looking a lot like new and extensible language keywords.

For example, here's a test case from some code I'm working on.

    public void test() throws Exception
        Method m = ComponentFixture.class.getMethod("getSpringBean");

        EnhancementOperation op = newMock(EnhancementOperation.class);
        IComponentSpecification spec = newMock(IComponentSpecification.class);
        Location l = newMock(Location.class);
        Capturer c = newCapturer(InjectSpecification.class);



        new InjectSpringAnnotationWorker().performEnhancement(op, spec, m, l);


        InjectSpecification is = c.getCaptured();

        assertEquals(is.getObject(), "someBeanName");
        assertEquals(is.getProperty(), "springBean");
        assertEquals(is.getType(), "spring");
        assertSame(is.getLocation(), l);

What I've found as I've been coding is that I make extensive use of static imports. First of all, Eclipse makes it easy to do (Ctrl-Shift-M on top of a static method converts it to a static import). Secondly, my personal coding style has been to use a goodly number of static methods (if I can define a new feature in terms of the existing public API of an object, and I don't expect there to be a need for subclasses to override, then I create a static method that takes the object as a parameter).

Those assert calls at the end are actually static methods on an Assert class. Some of the other methods, newCapture() and capture(), are also static. The newMock() calls are inherited from a base class. It actually looks better inside Eclipse, because the static methods are italicized.

My point is ... the code is looking more expressive to me, as some clutter falls away. Autoboxing reduces some more clutter. So does the new for loop, and variable argument lists. Generics are still something of a wash (you get more self-describing code by adding lots of clutter, but you gain by removing explicit casts). The end result follows my motto, though: Less is More.

Has this transformed the language? No, and perhaps that's the point. It's some incremental change, but it's makes for some small steps in the right direction. It's not quite the fluid flow of static class methods in Ruby (used for all kinds of meta-programming), but it's still nice.

On the other hand, Sun is much less aggresive in adopting changes to Java than, say, Microsoft. I hate to say it, but innovation (where innovation is defined as adopting features that other languages have had for a decade or so) seems to be occuring in C# before Java. In some cases, it is occuring in other languages that execute on the Java VM. The end result is the same sad story of Sun chasing Microsoft's tail.

I'd like to see some real improvements to the language. I want Ruby blocks and continuations, C# autotype variables, built-in Aspect support and, hey, maybe something I don't know about that will make my programming life better (maybe a pervasive hot class reloader). In this respect, Java always feels like a perpetual bridesmaid, never a bride.


MindBridge said...

While static imports are certainly useful in some cases, I am curious -- aren't static methods in direct contradiction to the notion of HiveMind? Specifically, inheritable methods with potentially several implementations?

Howard said...

There's a judgement call there ... can the static method truly encapsulate the behavior? Is there ever a need to subclass that behavior?

In fact, I've hit some limitations of this approach as well. You have to constantly re-test the code inside that static method when you are testing code that only uses the method. I see this with much of the enhancement code; I put static methods inside EnhanceUtils rather than on the EnhancementOperation interface ... and when I test objects that work with an EnhancementOperation, I'm always having to train the same extra couple of steps with my mocks.

I'm certainly seeing the most use of static imports inside test code (all those asserts!), and where I'm using static methods to enforce basic design-by-contract (something I expect to shift over into an Aspect at some point).

kiuma said...

If certainly static imports have the ability to extend java language, they also make confusion to an external programmer, since he can't know where your langauge extension begins and another ends.
Jurst for not speaking about average skilled programmers that don't even know the full java sintax.
You can have an example browsing source code into some common-lisp projects (http://www.cliki.net/index).

Like it always happens, more freedom /options complicate your and other life, so we should use static imports with extreme care.