Enum constant specific body and Getting the values of an enum


Enum constant specific body

In an enum it is possible to define a specific behavior for a particular constant of the enum which overrides the default behavior of the enum, this technique is known as constant specific body.

Suppose three piano students - John, Ben and Luke - are defined in an enum named PianoClass, as follows:

enum PianoClass {
	JOHN, BEN, LUKE;
	public String getSex() {
		return "Male";
	}
	public String getLevel() {
		return "Beginner";
	}
}

And one day two other students arrive - Rita and Tom - with a sex (Female) and level (Intermediate) that do not match the previous ones:

enum PianoClass2 {
	JOHN, BEN, LUKE, RITA, TOM;
	public String getSex() {
		return "Male"; // issue, Rita is a female
	}
	public String getLevel() {
		return "Beginner"; // issue, Tom is an intermediate student
	}
}

so that simply adding the new students to the constant declaration, as follows, is not correct:

PianoClass2 tom = PianoClass2.TOM;
PianoClass2 rita = PianoClass2.RITA;
System.out.println(tom.getLevel()); // prints Beginner -> wrong Tom's not a beginner
System.out.println(rita.getSex()); // prints Male -> wrong Rita's not a male

It's possible to define a specific behavior for each of the constant, Rita and Tom, which overrides the PianoClass2 default behavior as follows:

enum PianoClass3 {
	JOHN, BEN, LUKE,
	RITA {
	@Override
		public String getSex() {
			return "Female";
		}
	},
	TOM {
	@Override
		public String getLevel() {
			return "Intermediate";
		}
	};
	public String getSex() {
		return "Male";
	}
	public String getLevel() {
		return "Beginner";
	}
}

and now Tom's level and Rita's sex are as they should be:

PianoClass3 tom = PianoClass3.TOM;
PianoClass3 rita = PianoClass3.RITA;
System.out.println(tom.getLevel()); // prints Intermediate
System.out.println(rita.getSex()); // prints Female

Another way to define content specific body is by using constructor, for instance:

enum Friend {
	MAT("Male"),
	JOHN("Male"),
	JANE("Female");
 
	private String gender;
	Friend(String gender) {
		this.gender = gender;
	}
	public String getGender() {
		return this.gender;
	}
}

and usage:

Friend mat = Friend.MAT;
Friend john = Friend.JOHN;
Friend jane = Friend.JANE;
System.out.println(mat.getGender()); // Male
System.out.println(john.getGender()); // Male
System.out.println(jane.getGender()); // Female

Getting the values of an enum

Each enum class contains an implicit static method named values(). This method returns an array containing all values of that enum. You can use this method to iterate over the values. It is important to note however that this method returns a new array every time it is called.

public enum Day {
	MONDAY, TUESDAY, WEDNESDAY, THURSDAY, FRIDAY, SATURDAY, SUNDAY;
 
	/**
	* Print out all the values in this enum.
	*/
	public static void printAllDays() {
		for(Day day : Day.values()) {
			System.out.println(day.name());
		}
	}
}

If you need a Set you can use EnumSet.allOf(Day.class) as well.


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:

public interface ExtensibleEnum {
   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:

public enum DefaultValues implements ExtensibleEnum {
   VALUE_ONE, VALUE_TWO;
}

Additional values can then be defined like this:

public enum ExtendedValues implements ExtensibleEnum {
   VALUE_THREE, VALUE_FOUR;
}

Sample which shows how to use the enums - note how printEnum() accepts values from both enum types:

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

Note : 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.

Basic Programs