Psychedelic Panorama of Foo

Á¦ À̸§ÀÌ Inigo Montoya ÀÔ´Ï´Ù. ´ç½ÅÀÌ ³» ¾Æ¹öÁö¸¦ Á׿´¾î. Á×À» ÁغñÇØ.

Åä¿äÀÏ, 3¿ù 15, 2008

 

Static Imports and Constants in Java

¾Æ³çÇϼ¼¿ä. What are static imports? Well it only exits in Java 5.0 and later. It was originally intended as way around Constant Interface Antipattern. For more information see Sun's explanation of Static Imports.

Here's an example.

import static org.kuali.rice.RiceConstants.BLANK; . . . . . . String someString = BLANK;

Even though static imports exist to solve an antipattern, many developers regard them as being bad. I have decided to officially stamp my opinion on the matter and take a stance in support of static imports. That means this post is biased towards static imports.

To be fair, I first want to make a case against static imports and describe why many disapprove. The largest complaint is that it makes the code difficult to read. Take the aforementioned example. There could be a lot of code between the actual import statement and the place(s) where it is used. This means that if you are a noob and you want to know where BLANK is, you have to scroll all the way to the top of the file. Also, there are possible namespace issues. If a constant name is common through your application and third-party libraries, but is different in any of those cases, then static imports may not even be possible to use.

Now let me say that static imports actually improve the readability of code literally. "Used appropriately, static import can make your program more readable, by removing the boilerplate of repetition of class names." from Static Imports.Let me explain. Take the following quote from Edgar Allen Poe's "To Helen":

Clad all in white, upon a violet bank
  I saw thee half-reclining; while the moon
  Fell on the upturn'd faces of the roses,
  And on thine own, upturn'd--alas, in sorrow!

Now let's rewrite it using Kuali code which doesn't use the Constant Interface Antipattern, but puts all the constants in a single class.

import org.kuali.core.util.GlobalVariables; import org.kuali.kra.infrastructure.Constants; import org.kuali.kra.infrastructure.KraServiceLocator; . . . . . . List<Rose> roseBed = KraServiceLocator.getService(Constants.BO_SERVICE).findAll(Rose.class); Moon moon = GlobalVariables.getUserSession().getObject(Constants.MOON); if (KraServiceLocator.getService(Constants.MOON_SERVICE).isGlowingOn(moon, roseBed)) { boolean upturned = false; for (Rose rose : roseBed) { upturned |= Constants.UPTURNED.compareTo(rose.getState()) == 0; } if (upturned) { Person thee = KraServiceLocator.getService(Constatns.PERSON_SERVICE).getPerson(KraServiceLocator.getService(Constants.USER_SERVICE).getUserByUniversalId(Constants.THEE).getPersonId()); thee.setPalette(new ColorPalette(Constants.WHITE)); thee.setLocation(new Bank(new ColorPalette(Constants.VIOLET)); thee.setState(Constants.RECLINING); thee.inSorrow(true); } }

Now here is the case where static imports is used to simplify things.

import java.util.List; import static org.kuali.core.util.GlobalVariables.getUserSession; import static org.kuali.kra.infrastructure.Constants.BO_SERVICE; import static org.kuali.kra.infrastructure.Constants.MOON; import static org.kuali.kra.infrastructure.Constants.MOON_SERVICE; import static org.kuali.kra.infrastructure.Constants.PERSON_SERVICE; import static org.kuali.kra.infrastructure.Constants.RECLINING; import static org.kuali.kra.infrastructure.Constants.THEE; import static org.kuali.kra.infrastructure.Constants.UPTURNED; import static org.kuali.kra.infrastructure.Constants.USER_SERVICE; import static org.kuali.kra.infrastructure.Constants.VIOLET; import static org.kuali.kra.infrastructure.Constants.WHITE; import static org.kuali.kra.infrastructure.KraServiceLocator.getService; . . . . . . List<Rose> roseBed = getService(BO_SERVICE).findAll(Rose.class); Moon moon = getUserSession().getObject(MOON); if (getService(MOON_SERVICE).isGlowingOn(moon, roseBed)) { boolean upturned = false; for (Rose rose : roseBed) { upturned |= UPTURNED.compareTo(rose.getState()) == 0; } if (upturned) { Person thee = getService(PERSON_SERVICE).getPerson(getService(USER_SERVICE).getUserByUniversalId(THEE).getPersonId()); thee.setPalette(new ColorPalette(WHITE)); thee.setLocation(new Bank(new ColorPalette(VIOLET)); thee.setState(RECLINING); thee.inSorrow(true); } }

You can see in the first example, the readability is greatly limited by KraServiceLocator, Constants, and GlobalVariables allover the place. Static imports in the next part greatly reduces this and makes it much more readable literally. Keep in mind though, that this really only makes sense because of the number of times KraServiceLocator and Constants are used. If there are only 2 cases in the code, then it would be really silly to use a static import. It's almost a waste of time. However, the most common case is the one illustrated above where Constants and KraServiceLocator are used a lot. For you it may be different classes, but if you are doing any of the following:

  • Sticking all your constants in a central file
  • Using a large number of constants from a single class
  • Using one constant again and again.

... then, you definitely need static imports.

I have a solution for those that have trouble with static imports because they don't like to scroll. If you're using Eclipse, you can just hold the Ctrl with your mose over the constant. It will highlight and take you directly to where it is declared at the top of the code. Follow the same procedure again, and it will then take you to the source file where the constant is cross-referenced. If you hold the Alt button and press the back arrow, it will take you back. Press it again, and it will go back again to where you originally were. Very fast for browsing code. Of course, if you followed the rules outlined above, then you don't need to know where the constant is defined. You should already know because it is used everywhere.

I have further problems with needing to know where the constant is defined. Why does a developer need to know straight away without thinking where the constant exists. I think it is more important to know what the constant represents than where it is defined. Take for example, Constants.ZERO. I care more that it is "ZERO" than whether Constants.ZERO = "0" or Constants.ZERO = "0.0" or that Constants.ZERO is in org.kuali or org.apache. What I care most about is that it is "ZERO." My above information is a solution to that in both cases whether you are using static imports or not. Not that this is always the case. I can understand that if something is showing 0.0 and it should be 0, then it would probably matter where that is, but that's what logging and debuggers are for. But if we can rely on constants being in the same class, we shouldn't need to qualify that class. Just like we don't need to qualify the package name the class is in.

I think that if a constant is encountered that isn't in Constants, then it should probably be qualified for clarity. Take for example, Math.PI vs. Constants.PI. I think Constants.PI should probably not be qualified, but if someone decides they want to use Math.PI (for example:) if (PI == Math.PI) ...

Static imports aren't bad. They're actually good, but keep in mind that they can be abused. Please don't ruin it for the rest of us.

·¹À̺í: , ,





This page is powered by Blogger. Isn't yours?

¿¡ °¡ÀÔ °Ô½Ã¹° [Atom]