Enumerations
Java enums (declared using the enum keyword) are shorthand syntax for sizable quantities of constants of a single class. Enumerated values are used to represent a set of named values. Historically in Java (and other languages), these were often stored as constants. For example:
``` public static final int SEASON_SPRING = 0; public static final int SEASON_SUMMER = 1; public static final int SEASON_FALL = 2; public static final int SEASON_WINTER = 3;
There are, however, a number of issues with this approach:
* Acceptable values are not obvious
* Since the values are just integers, it’s hard at a glance to tell what the possible values are.
* No type safety
* Since the values are just integers, the compiler will let you substitute any valid integer.
* No name-spacing
* With our seasone example, we prefixed each of the suits with “SEASON_”
* We chose to prefix all of those constants with this prefix to potentially disambiguate from other enumerated values of the same class
* Not printable
* Since they are just integers, if we were to print out the values, they’d simply display their numerical value
## basic enum
Enum can be considered to be syntax sugar for a sealed class that is instantiated only a number of times known at compile-time to define a set of constants. A simple enum to list the different seasons would be declared as follows:
WINTER,
SPRING,
SUMMER,
FALL }
While the enum constants don't necessarily need to be in all-caps, it is Java convention that names of constants are entirely uppercase, with words separated by underscores.
You can declare an Enum in its own file (the following enum is declared in the Season.java file) :
WINTER,
SPRING,
SUMMER,
FALL }
## Usage
We can declare the enum alone or inside another class. we cannot declare an Enum inside a method body or constructor.
Every constant of enum is **public**, **static** and **final** by default. As every constant is static, they can be accessed directly using the enum name.
Enum constants can be passed around as method parameters:
// name() is a built-in method that gets the exact name of the enum constant System.out.println(s.name());
} display(Season.WINTER); // Prints out "WINTER"
You can get an array of the enum constants using the `values()` method. The values are guaranteed to be in declaration order in the returned array. It is important to note however that this method returns a new array every time it is called:
for (Season s : Season.values()) {
System.out.println(s.name());
} } public static void enumSwitchExample(Season s) {
switch(s) {
case WINTER:
System.out.println("It's pretty cold");
break;
case SPRING:
System.out.println("It's warming up");
break;
case SUMMER:
System.out.println("It's pretty hot");
break;
case FALL:
System.out.println("It's cooling down");
break;
} } Season.FALL == Season.WINTER // false Season.SPRING == Season.SPRING // true
Another way to compare enum constants is by using equals() as below, which is considered bad practice as you can easily fall into pitfalls as follows:
## Enums with Constructors
An enum cannot have a public constructor; however, private constructors are acceptable (constructors for enums are package-private by default):
PENNY(1), NICKEL(5), DIME(10), QUARTER(25); // usual names for US coins
// note that the above parentheses and the constructor arguments match
private int value;
Coin(int value) {
this.value = value;
}
public int getValue() {
return value;
}
}
int p = Coin.NICKEL.getValue(); // the int value will be 5
It is recommended that you keep all fields private and provide getter methods, as there are a finite number of instances for an enum.
If you were to implement an Enum as a class instead, it would look like this:
public static final Coin PENNY = new Coin(1);
public static final Coin NICKEL = new Coin(5);
public static final Coin DIME = new Coin(10);
public static final Coin QUARTER = new Coin(25); private int value; private Coin(int value){
this.value = value;
} public int getValue() {
return value;
} } int p = Coin.NICKEL.getValue(); // the int value will be 5
## Enums with Abstract Methods
Enums can define abstract methods, which each enum member is required to implement.
DODGE { public boolean execute(Player player) {
return player.isAttacking();
}
},
ATTACK {
public boolean execute(Player player) {
return player.hasWeapon();
}
},
JUMP {
public boolean execute(Player player) {
return player.getCoordinates().equals(new Coordinates(0, 0));
}
};
public abstract boolean execute(Player player); }
This allows for each enum member to define its own behaviour for a given operation, without having to switch on types in a method in the top-level definition.
Note that this pattern is a short form of what is typically achieved using polymorphism and/or implementing interfaces.
## Implements Interface
This is an enum that is also a callable function that tests String inputs against precompiled regular expression patterns.
System.out.println(Acceptor.NULL.test(null)); // true
System.out.println(Acceptor.EMPTY.test("")); // true
System.out.println(Acceptor.NULL_OR_EMPTY.test(" ")); // false
} }
## Implement Singleton with enum
Enum constants are instantiated when an enum is referenced for the first time. Therefore, that allows to implement Singleton software design pattern with a single-element enum.
According to "Effective Java" book by Joshua Bloch, a single-element enum is the best way to implement a singleton.
This approach has following advantages:
* thread safety
* guarantee of single instantiation
* out-of-the-box serialization
And as shown in the section implements interface this singleton might also implement one or more interfaces.
## Using methods and static blocks
An enum can contain a method, just like any class. To see how this works, we'll declare an enum like this:
switch (this){
case NORTH:
return SOUTH;
case SOUTH:
return NORTH;
case WEST:
return EAST;
case EAST:
return WEST;
default: //This will never happen
return null; }
} } public enum Direction { NORTH, SOUTH, EAST, WEST;
private Direction opposite;
public Direction getOpposite() { return opposite; }
static { NORTH.opposite = SOUTH; SOUTH.opposite = NORTH; WEST.opposite = EAST; EAST.opposite = WEST; } }
In this example, the opposite direction is stored in a private instance field opposite, which is statically initialized the first time a Direction is used. In this particular case (because `NORTH` references `SOUTH` and conversely), we cannot use Enums with constructors here (Constructors `NORTH(SOUTH)`, `SOUTH(NORTH)`, `EAST(WEST)`, `WEST(EAST)` would be more elegant and would allow opposite to be declared final, but would be self-referential and therefore are not allowed).
## Zero instance enum
Just as enum can be used for singletons (1 instance classes), it can be used for utility classes (0 instance classes). Just make sure to terminate the (empty) list of enum constants with a ;.
/ No instances /;
public static int clamp(int min, int max, int i) {
return Math.min(Math.max(i, min), max);
}
// other utility methods... }
## Enum Polymorphism Pattern
When a method need to accept an "extensible" set of enum values, the programmer can apply polymorphism like on a normal class by creating an interface which will be used anywere where the enums shall be used:
String name(); }
This way, any enum tagged by (implementing) the interface can be used as a parameter, allowing the programmer to create a variable amount of enums that will be accepted by the method. This can be useful, for example, in APIs where there is a default (unmodifiable) enum and the user of these APIs want to "extend" the enum with more values.
A set of default enum values can be defined as follows:
VALUE_ONE, VALUE_TWO; } public enum ExtendedValues implements ExtensibleEnum {
VALUE_THREE, VALUE_FOUR; } private void printEnum(ExtensibleEnum val) {
System.out.println(val.name()); }
printEnum(DefaultValues.VALUE_ONE); // VALUE_ONE printEnum(DefaultValues.VALUE_TWO); // VALUE_TWO printEnum(ExtendedValues.VALUE_THREE); // VALUE_THREE printEnum(ExtendedValues.VALUE_FOUR); // VALUE_FOUR
This pattern does not prevent you from redefining enum values, which are already defined in one enum, in another enum. These enum values would be different instances then. Also, it is not possible to use switch-on-enum since all we have is the interface, not the real enum.
## EnumSet
To group, complement, range the enum values we have EnumSet class which contains different methods.
* **EnumSet#range** : To get subset of enum by range defined by two endpoints
* **EnumSet#of** : Set of specific enums without any range. Multiple overloaded of methods are there.
* **EnumSet#complementOf** : Set of enum which is complement of enum values provided in method parameter
public class Test { public static void main(String[] args) {
EnumSet<Page> range = EnumSet.range(Page.A1, Page.A5);
if (range.contains(Page.A4)) {
System.out.println("Range contains A4");
}
EnumSet<Page> of = EnumSet.of(Page.A1, Page.A5, Page.A3);
if (of.contains(Page.A1)) {
System.out.println("Of contains A1");
}
} } ```
Exercise
-
Exercise 1:
- Create a public enum Weekday with constants for MONDAY, TUESDAY,... until SUNDAY.
- The enum should have an instance method boolean isWeekDay() and an instance method boolean isHoliday().
- The
isHoliday()
method should return the opposite of isWeekDay(). - Write a program which demonstrates how this enum could be used, which has a method which takes a Weekday as the argument and prints a message depending on whether the Weekday is a holiday or not.
- Create a public enum Weekday with constants for MONDAY, TUESDAY,... until SUNDAY.
-
Exercise 2:
- Use your weekday class to investigate if enums implement Comparable.
- Declare a Weekday sat = Weekday.SATURDAY;.
- Use a loop over Weekday.values() with day as loop variable and print out each value and whether it is less than, equal to or greater than sat, using the call day.compareTo(sat) - remember that the compareTo() method returns an int such that
- a negative value means that day is less than sat
- zero means that day is considered equal to sat (when comparing them on order)
- a positive value means that day is greater than sat
- Use your weekday class to investigate if enums implement Comparable.