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 Weekday
s 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.