Wednesday, April 11, 2012

Are Static Imports Becoming Increasingly Accepted in Java?

There was a time when the use of the word ain't was widely considered unacceptable, at least in polite society. Indeed, at that time (and perhaps still today), many people did (and do) not consider ain't to be a real word. Although the word ain't remains controversial and its usage is still often considered improper, it seems to be slowly gaining general acceptance and holding its own in terms of frequency of use. At one time, it was used intentionally for emphasis by "people who know better," but seems to be slowly gaining in terms of popularity. In many ways, the static import introduced with J2SE 5 seems to be treated similarly to use of the word ain't.

The J2SE 5 Programming Language Guide's section on Static Imports is famously quoted (emphasis is part of original): "So when should you use static import? Very sparingly!" The section's final paragraph describes when static import might be preferable:

So when should you use static import? Very sparingly! Only use it when you'd otherwise be tempted to declare local copies of constants, or to abuse inheritance (the Constant Interface Antipattern). In other words, use it when you require frequent access to static members from one or two classes. If you overuse the static import feature, it can make your program unreadable and unmaintainable, polluting its namespace with all the static members you import. Readers of your code (including you, a few months after you wrote it) will not know which class a static member comes from. Importing all of the static members from a class can be particularly harmful to readability; if you need only one or two members, import them individually. Used appropriately, static import can make your program more readable, by removing the boilerplate of repetition of class names.

Like the word "ain't," the educated Java developer masses seem to be almost universally in agreement that the static import should be used sparingly. The reasoning here is obvious. First, the official documentation says so. Second, and far more importantly, there is no question that overuse of static import might actually lead to less readable code even if it's more concise code. In fact, too much static importing might lead to collisions that will remove the ability to use static imports so much. Despite awareness and acknowledgement of the evils and potential abuses of the static import, its use seems to be growing in the Java community.

When writing simple examples to illustrate points (such as for posts on this blog), I frequently don't bother using a logging framework and instead resort to simple use of System.out and System.err. I don't mind making the assumption that any reference to out in my code refers to the handle to the standard output and that any reference to err refers to the handle to the standard error. I don't plan to use out or err in any other context, so this brings conciseness to the simple code without loss of readability or adding of ambiguity. It also feels very much like Groovy's approach (although not quite as concise as that) to writing to standard output. You can find more details on this approach in Java static import: System.out and err, in my post Static Imports and System.out, and in Cay Horstmann's post Are you using static import?

Perhaps even more prevalent use of static import in the Java world occurs in the name of unit testing. Several of the most popular Java-oriented unit testing frameworks encourage use of static imports for more fluent test code. JUnit's Assert methods, Mockito's Mockito methods, and Hamcrest's Matchers are some of the most obvious examples of the prevalence of static import usage in the Java unit testing world.

In the post My dislike of Java’s static import, Mark Needham described a situation I think many Java development shops find themselves in when it comes to static imports:

On my last project we ended up saying that import static was allowed in test code because there were relatively few places the static methods could be imported from, but when it came to production code the fully qualified path was required.

Even use of static imports in test code is not without issue or controversy. The StackOverflow thread Finding import static statements for Mockito constructs talks about some frustrations related to the use of static imports. Needham also addressed this issue:

The benefit of this approach is that it makes the code read more fluently but the disadvantage is that you can’t immediately tell where a method lives. I want to be able to tell what is going on in the code from looking at it and anything which prevents this is a hindrance.

So far, I've looked at use of Java static imports in association with calls to java.lang.System.out and in association with unit testing. Both of these cases are not typical production code cases (logging with a logging framework is preferable in production to standard output and unit tests are not production code, though they may be delivered with it).

It is perhaps less obvious which Java frameworks intended for production code encourage use of static imports. One example is lambdaj. The lambdaj features Wiki page starts off by recommending use of static imports:

All those features are provided as static methods in the class Lambda, so the best way to use them is just to add the following import:

import static ch.lambdaj.Lambda.*;

in the classes where you want to use it.

The more general use case for Java use of static import is the development of Domain-Specific Languages (DSLs) in Java. In many ways, the use of static imports already discussed in this post for JUnit, Mockito, Hamcrest, and Lambdaj are specific examples of this more general trend toward fluent interfaces and DSLs.

For good reason, I believe that most Java developers are cautious about overuse and abuse of static imports. However, increased use of static imports in appropriate circumstances seems to be the result of playing with these and learning the positives and negatives of them. The rise of the JVM scripting languages and other more concise (less ceremony) languages has also probably affected the general thinking about use of static imports.

The drive for fluent interfaces (positive effect of static imports) must be compared to the maintenance and readability costs associated with use of static imports. In general, just as I think "ain't" is still generally frowned upon but is perhaps less frowned upon than it used to be, I also think that static imports are still generally discouraged, but that perhaps we as a Java community have started to see situations in which they might be okay or even a positive feature worth their cost. I don't think anyone thinks it's a good idea to use them frequently and without regard to the context in which they are being used.

8 comments:

Joe said...

A static method's or constant's container class name is just another part of its namespace. And as long as you're not using * imports, you'll have all the fully qualified names spelled out for you in the import section. If you're constantly dealing with naming conflicts or find yourself confused as to where exactly static method foo() is defined, then your real problem is likely that your class is too huge, not that you're abusing static imports.

Lukas Eder said...

Very nice article. I share your feelings for this language feature, which has greatly helped jOOQ become popular, as it allows for pretending SQL is available in Java as an "internal domain specific language"

@DustinMarx said...

Lukas,

After looking a little at jOOQ, I agree that it's a great example of using the static import to create the DSL feel (in this case, SQL within Java).

Your Single Page Manual puts it well: "With jOOQ 2.0, static factory methods have been introduced in order to make your code look more like SQL. Ideally, when working with jOOQ, you will simply static import all methods from the Factory class."

By the way, I like the slogan, "A peace treaty between SQL and Java" and the idea of this project.

Thanks for sharing.

Dustin

@DustinMarx said...

Joe,

You definitely make some good points and I think your comment is a good reminder of the importance of using the correct features in the correct context and also using the features correctly. One of the reasons the unit testing facilities probably do so well with static imports is that the context (unit testing) by its nature limits the potential static import conflicts.

Dustin

Johannes Brodwall said...

An observation from the sideline: it seems like all the arguments against static imports could be arguments against class imports as well.

As you point out, namespace conflicts are symptoms of underlying problems. The same holds true for class imports...

Ondrej Medek said...

The number of class names is order is magnitude less than method names. That's why static imports should be used very very rarely, but class imports are common practise.

Most modern IDE, editors, have customizable code completion. So I do not need static imports. I just customize the editor, write something like "syserr" and the editor changes it with "System.err.println()".

Unknown said...

I use static imports every chance I get. The only feature from Scala that I want in Java is transitive static imports.

@DustinMarx said...

There is a discussion regarding use of Java static imports in the reddit thread "Why aren't we all using static imports?"