Constant Encapsulation

One final thing to note about constant objects is that they allow you a great deal of flexibility. You can put any kind of data in a derived constant object. For example, you may want abbreviations of commonly referenced country names. Example 7-14 shows an expanded constant object class with country abbreviations.

Example 7-14. Countries with abbreviations
package oracle.hcj.constants;
public final class Country7 extends ConstantObject {
 public static final Country7 CANADA = new Country7("CANADA", "CA");
 public static final Country7 CROATIA = new Country7("CROATIA", "HR");
 public static final Country7 GERMANY = new Country7("GERMANY", "DE");
 public static final Country7 ITALY = new Country7("ITALY", "IT");
 public static final Country7 MEXICO = new Country7("MEXICO", "MX");
 public static final Country7 UK = new Country7("UK", "UK");
 public static final Country7 USA = new Country7("USA", "US");
 public static final Country7 VENEZUELA = new Country7("VENEZUELA", "VZ");
 /** Holds the abbreviation for the Country. */
 private final String abbreviation;
 private Country7(final String name, final String abbreviation) {
 super(name);
 this.abbreviation = abbreviation;
 }
 public final String getAbbreviation( ) {
 return this.abbreviation;
 }
}


Adding the abbreviation information is easy. You retain all the benefits of constant objects and can define more information than a simple cryptic number. There is practically no limit to the information you can add to constant objects. However, like any power tool, constant objects can hurt you if you're not careful. Example 7-15 shows a trap you need to watch out for.

Example 7-15. A country object with a read/write property
package oracle.hcj.constants;
public final class Country8 extends ConstantObject {
 public static final Country8 CANADA = new Country8("CANADA", 31.90f);
 public static final Country8 CROATIA = new Country8("CROATIA", 4.39f);
 public static final Country8 GERMANY = new Country8("GERMANY", 83.25f);
 public static final Country8 ITALY = new Country8("ITALY", 57.71f);
 public static final Country8 MEXICO = new Country8("MEXICO", 103.40f);
 public static final Country8 UK = new Country8("UK", 59.77f);
 public static final Country8 USA = new Country8("USA", 280.56f);
 public static final Country8 VENEZUELA = new Country8("VENEZUELA", 24.28f);
 /** Holds the abbreviation for the Country in millions. */
 private float population;
 private Country8(final String name, final float population) {
 super(name);
 this.population = population;
 }
 public final void setPopulation(final float population) {
 this.population = population;
 }
 public final float getPopulation( ) {
 return this.population;
 }
}


In this code, the programmer added the population of the country to the constant object subclass Country8. Since he was aware that populations change, he made population a read/write property so he can update the populations as necessary. However, having read/write properties in constant objects is a bad thing to do for a number of reasons, the most important being that it violates the object-oriented paradigm that constant objects represent. A constant object should be entirely constant. Objects that are partially constant and partially nonconstant complicate the situation because the user of a constant object expects things to stay the way they are and not change from day to day. Additionally, the read of these constant objects via serialization is now ambiguous. Which population is the correct one? Is it the one in the file being read or the one already in the virtual machine? Does it depend on when the constant was written to the disk? These are just a couple of the questions that the confused programmer would have to deal with when using mixed-mode objects. It is best to avoid these objects completely in your code. Instead of adding variable information to constant objects, create a new class called CountryInformation with a member that is a constant object and other members that contain the variable information. This provides a cleaner separation and less confusion. Speaking of separation, you should also consider one other common mistake shown in Example 7-16.

Example 7-16. Mixed constants
package oracle.hcj.constants;
public class OptionConstant extends ConstantObject {
 /** An error message type. */
 public static final OptionConstant ERROR_MESSAGE =
 new OptionConstant("ERROR_MESSAGE");
 /** An information message type. */
 public static final OptionConstant INFORMATION_MESSAGE =
 new OptionConstant("INFORMATION_MESSAGE");
 /** An warining message type. */
 public static final OptionConstant WARNING_MESSAGE =
 new OptionConstant("WARNING_MESSAGE");
 /** An question message type. */
 public static final OptionConstant QUESTION_MESSAGE =
 new OptionConstant("QUESTION_MESSAGE");
 /** An plain message type. */
 public static final OptionConstant PLAIN_MESSAGE =
 new OptionConstant("PLAIN_MESSAGE");
 /** Use default buttons. */
 public static final OptionConstant DEFAULT_OPTION =
 new OptionConstant("DEFAULT_OPTION");
 /** Use yes/no buttons. */
 public static final OptionConstant YES_NO_OPTION =
 new OptionConstant("YES_NO_OPTION");
 /** Use yes/no/cancel buttons. */
 public static final OptionConstant YES_NO_CANCEL_OPTION =
 new OptionConstant("YES_NO_CANCEL_OPTION");
 /** Use ok/cancel buttons. */
 public static final OptionConstant OK_CANCEL_OPTION =
 new OptionConstant("OK_CANCEL_OPTION");
 public OptionConstant(String name) {
 super(name);
 }
}


In this class, several constant objects are declared. There are five constants for the type of message that is displayed and four constants for the type of buttons to use. The problem with this class is that the constants are mixed together. Imagine what could happen if a user gave OK_CANCEL_OPTION as a message type. The programmer of this constant object class should have separated the constants into distinct classes, each with its own purpose, instead of mixing them. Those of you who do a lot of Swing coding may recognize this as the ConstantObject rendering of the constants in the JOptionPane class. However, the real JOptionPane uses integral constants for each of the options, making the situation worse. For example, a user of the JOptionPane class may want to make a JOptionPane of the yes/no warning variety. So he writes the following code:

JOptionPane.showOptionDialog(null, "Save before Exiting?", "Warning", JOptionPane.WARNING_MESSAGE,
 JOptionPane.YES_NO_CANCEL_OPTION, myIcon, null, null);


However, since the programmer accidentally put the message type in the button options parameter and the button options in the message type parameter, he is presented with a information message with OK and Cancel buttons. The compiler doesn't catch the error, and something trivial ends up being an annoying bug. If the options to JOptionPane were constant objects separated by type, this wouldn't happen. As long as you remember not to mix various types of constant objects together or to make mixed-mode constant/nonconstant objects, the ConstantObject paradigm will be an enormous asset to the development of your programs. Constant objects provide much more functionality, security, and safety than integral option constants.

      
Comments