Java Enums: make your code clean.

Ksenia Jakubowska
4 min readMar 15, 2021

Every developer heard about special type in Java language — Enum. Most people imagine following structure when heard this word

public enum Pokemon {
PIKACHU, CHARMANDER, EEVEE
}

indeed, this is the simpliest way to create Enum. Such structure provides us an easy view what pokemon we could use: Pikachu, Charmander and Eevee. There will be never Charizard or Snorlax. Basic configuration allows us to assign new pokemon and compare existing ones:

Pokemon pokemon = myWorld.getPokemon();if (PIKACHU == pokemon) {
System.out.println("Pokemon is Pikachu");
} else if (CHARMANDER == pokemon){
System.out.println("Pokemon is Charmander. Switching to Pikachu");
myWorld.setPokemon(Pokemon.PIKACHU);
}

As you noticed all values are uppercased as they will never change and constants in Java are always uppercased due to the naming convension.

Could we benefit from enums more? Definitely! We could not only define what pokemons we could work with but also define attack specific to each pokemon. All this in one place! As enum is still a Java class we could create a private field that will be instantiated during enum creation. We are creating objects inside enum itself so we will use private constructor (if no access modifier is defined than constructor will be private by default). As pokemon’s strongest attack is never changed we will make this field final. We will also add a method for getting attack straight out of our enum.

public enum Pokemon {
PIKACHU ("Thunder Shock"),
CHARMANDER ("Scratch"),
EEVEE ("Sand Attack");
private final String attack;

private Pokemon(String attack) {
this.attack = attack;
}
public String getAttack() {
return this.attack;
}
}

If I be a happy owner of pokemon I would definitely try their attack skills like this:

List<Pokemon> myPokemons = List.of(Pokemon.PIKACHU, Pokemon.EEVEE);
myPokemons.forEach(Pokemon::getAttack);

Compare this nice and clean function call with straight-forward if-else solution.

List<Pokemon> myPokemons = List.of(Pokemon.PIKACHU, Pokemon.EEVEE);
for (Pokemon pokemon: myPokemons){
if (pokemon == PIKACHU) {
return "Thunder Shock";
} else if (pokemon == CHARMANDER) {
return "Scratch";
} else if (pokemon == EEVEE) {
return "Sand Attack";
}
throw IllegalArgumentException("No such Pokemon: " + pokemon);
}
}

What is bad with this solution will you ask? Firstly, logic that is related to Pokemon is somewhere else. Secondly, if we iterate over all possible values we need to handle a case when there is no matching value. And finally all these else-if cases just adding unnecessery complexity to the code. Moreover, if we want to add additional information about our pokemon, like weight, height, colour, we would need another else-if block somewhere in our business logic. Each new property will bring us closer to spaghetti code.

Enums could have as many properties as we like. Properties could be simple Java types as well as any Java object including collections and other enums. But could some pokemon have properties that others not? The answer is yes. Consider following case: each pokemon could level up. Baby Pikachu is called Pichu. Charmander and Eevee do not have baby pokemons. If we want to reflect this we have to create two constructors: first with one parameter and second with both.

public enum Pokemon {
PIKACHU ("Thunder Shock", "Pichu"),
CHARMANDER ("Scratch"),
EEVEE ("Sand Attack");
private final String attack;
private final String baby;

private Pokemon(String attack) {
this.attack = attack;
this.baby = null;
}
private Pokemon(String attack, String baby) {
this.attack = attack;
this.baby = baby;
}
public String getAttack() {
return this.attack;
}
public String getBaby() {
return this.baby;
}
}

Another useful thing that could be done within enum is getting enum by parameter. We could level up our baby pokemon and then attack with new one. In enum we need to add additional method fromBaby that will accept baby pokemon as a parameter. Iterating over all possiable enum values (there is special enum method values() ) we could get matching pokemon. After that we could just call atack method on matching value.

public enum Pokemon {
.........
@Nullable
public Pokemon getFromBaby(@NotNull String baby) {
for (Pokemon pokemon: values()) {
if (baby.equals(pokemon.getBaby()) {
return pokemon;
}
}
return null;
}
}

......
var babyPokemon = "Pichu";
var adultPokemon = Pokemon.getFromBaby(babyPokemon);
if (adultPokemon != null) {
adultPokemon.getAttack();
}

Even though enums are special Java types they still have a lot in common with regular Java classes. Do not hesitate to extend your enums with a few constructors, private fields or additional methods. Keep in mind that Enums should not contain heavy business logic. You could use Enums for properties: error codes, set of colors. But if you know that the list of properties often changes Enums are not recommended as well.

--

--