B669: Personalized Data Mining and Mapping
Protect Yourself

 

You've probably already learned the trick of using interfaces to keep track of common constants in a group of classes. For example, instead of having to set constants using code like:


public final static int SUNDAY = 0;
public final static int MONDAY = 1;
//etc

in every class you need to refer to them, you can just declare, say, a Weekday interface, like so:

interface Weekday
	{
	public final static int SUNDAY = 0;
	public final static int MONDAY = 1;
	//etc
	}

Then to use these constants in a daytimer or calendar or whatever you can just have whatever classes that need to know about weekdays implement the Weekday interface. That way they can all refer to Weekday.SUNDAY (or, even more shortly, just SUNDAY) and be done with it. Further, you don't have to go through all your code and change the settings if for some reason you want to alter the values kept in the Weekday constants.

This is a good, but not great, solution. The problem is that the compiler isn't doing any type checking for you. If you implement the Weekday interface you can freely use and test against SUNDAY, but (a) you always have to remember that it's really just an int and (b) you could, if you wanted, assign any old int to an int variable named "weekday" and the compiler wouldn't complain since there's no connection between that variable and the values you can put in it.

This means that you're leaving a hole open for bugs to skitter in. The next programmer to modify your code, for example, might do something stupid like declaring a yesterday variable and calculating it by subtracting one from a today variable then forgetting to check that today isn't SUNDAY. And, of course, that stupid "next programmer" could be you. (This actually happened to me!)

Such bugs can sneak in because the Weekday interface is not a firm contract between your classes. It's just a typical C-like hack.

What we want instead is some way to tell the compiler all about the special properties that any Weekdays must have and let the compiler worry about checking that we always adhere to those properties. In other words, we need to make Weekday into a true type.

Here's a solution to that problem that completely eliminates all holes:


public final class Weekday //disallow subclassing
	{
	public final static Weekday SUNDAY = new Weekday();
	public final static Weekday MONDAY = new Weekday();
	//etc

	private Weekday() //disallow outside instantiation
		{}

	public String toString()
		{
		if (this == SUNDAY)
			return "SUNDAY";
		else
		if (this == MONDAY)
			return "MONDAY";
		//etc
		}
	}

Making class Weekday final makes it unsubclassable, which prevents anyone from subclassing it, monkeying with the subclass's properties, then passing an instance of the subclass as an instance of class Weekday and thereby messing up all your carefully laid checks.

Declaring the Weekday constructor prevents the compiler from automatically inserting an empty (and public) constructor. Then making the constructor private means that only class Weekday can instantiate objects of class Weekday.

So for the cost of making seven objects, instead of seven ints, all type-checking is done for you by the compiler. (The compiler needs the work!)

The beauty of this scheme only becomes really clear when you realize that you can also declare variables to be of type Weekday and have full type checking, again by the compiler.

For example, in any class that imports class Weekday you can say,


private Weekday weekday = Weekday.SUNDAY;

and use that Weekday variable just as you would any other variable. The special thing you know about this variable is that it cannot assume more than seven different values. What those values are is completely irrelevant; all you need to know is their names. Bugs don't have a chance to invade this code---it's completely bulletproof.

One further advantage is that since it's now a class and not an interface, you can add methods to class Weekday to do whatever Weekday things that might need doing (for example, calculating what day yesterday is) and leave it all inside class Weekday.

There is no longer any possibility of assigning an incorrect value to a Weekday variable, nor is there ever any possibility of incorrectly testing against a Weekday constant. Class Weekday is now fully encapsulated and safe as houses.