Week 2: enums, built from "what stops a typo from becoming a bad value?"
By the end you can replace a field that should hold one of a fixed set of values with an enum, so an illegal
value is a compile error instead of a runtime surprise, and you can give an enum constant its own data and its
own behavior. It builds on the class you already write and points straight at the real Course, whose term
field is exactly this.
The user story behind this. A department coordinator wants a section to refuse a meeting day that does not
exist, so that a typo like Mnday is caught the moment it is typed, not discovered when a student shows up to
an empty room. The set of weekdays is fixed and known. The coordinator wants the type system to enforce that,
not their own vigilance.
Before you start
- [ ] I can write a class with a private final field set in the constructor.
- [ ] I can call a method on an object and use what it returns.
- [ ] I know that
==on twoStringobjects is unreliable, and.equalscompares contents.
The problem (sit with this before reading on)
A Course needs a term. The quickest field type is a String:
private final String term; // "SUMMER", maybe
Every one of these compiles and runs: "SUMMER", "summer", "Sumemr", "Q3", "". Only one of them is
what you meant, and nothing catches the other four until something downstream breaks on a value it does not
recognize. The set of real terms is small and fixed, but a String field does not know that.
Check yourself. The term field is a String. In one sentence, why does a typo like "Sumemr" survive all
the way to runtime?
Atom 1: an enum is a type with a fixed set of named values
An enum is a class whose instances are a fixed, named set, listed once:
public enum Term {
FALL, WINTER, SPRING, SUMMER;
}
Now the term field has type Term, and the only values that exist are the four you named. Term.SUMMER is the
one way to say summer, and Term.Sumemr is not a typo that slips through, it is a compile error, because there
is no such constant. The set is closed, so an illegal value cannot be constructed at all.
Check yourself. With private final Term term, what happens if you write Term.Sumemr, and how is that
different from the String version?
Atom 2: each constant is a single shared instance
There is exactly one Term.SUMMER object for the whole program, and every mention of it is that same one
instance. This is why == is safe and correct on enum constants, where it was unreliable on strings:
term == Term.SUMMER asks the identity question, and for enums identity and value are the same thing, because
there is only one of each.
Check yourself. In one sentence, why is term == Term.SUMMER a safe comparison when name == "SUMMER" on
a String was not?
Atom 3: a constant can carry its own data
An enum constant can hold state. Give the enum a field and a constructor, and pass each constant its own value in the list:
public enum Term {
FALL("Fall"), WINTER("Winter"), SPRING("Spring"), SUMMER("Summer");
private final String label;
Term(String label) { this.label = label; }
public String label() { return label; }
}
Now Term.SUMMER.label() returns "Summer", a human-readable name that lives with the constant instead of in
a lookup somewhere else. The enum is still a closed set; it just carries a little data on each member.
Check yourself. Term.SUMMER.label() returns "Summer". In one sentence, where does that string live, and
why is that better than a separate if that turns SUMMER into "Summer"?
Atom 4: a constant can carry its own behavior
A constant can do more than hold data; it can answer a question differently per constant. A campus rule is the classic case: some campuses require teaching authorization and some do not.
public enum Campus {
CHENEY { public boolean requiresAuthorization() { return false; } },
SPOKANE { public boolean requiresAuthorization() { return true; } };
public abstract boolean requiresAuthorization();
}
Each constant gives its own answer, so the rule lives on the constant instead of in a long if that has to
name every campus. Add a campus and the compiler makes you give its answer, because the method is abstract.
This is the reason to reach for an enum over constants of a plain type: the behavior travels with the value.
Check yourself (competency close). Finish in your own words: "An enum is a type with , so a typo becomes a ___ instead of a . Putting data and behavior on a constant means the rule lives ___ instead of in ___."
What you collected
- An enum is a type whose values are a fixed named set, so an illegal value is a compile error.
- Each constant is a single shared instance, which is why
==is safe on enums. - A constant can carry its own data, such as a display label, kept with the value.
- A constant can carry its own behavior, so a per-value rule lives on the constant, not in a scattered
if.
Where this is going
You can now hold one of a fixed set safely. Next is the other side of holding values: a growable list of many
of them, the ArrayList the library gives you, and the List interface it implements. That is the last Week 2
lesson.