Over a million developers have joined DZone.
{{announcement.body}}
{{announcement.title}}

This powerful Enum

DZone's Guide to

This powerful Enum

Free Resource

Download our Introduction to API Performance Testing and learn why testing your API is just as important as testing your website, and how to start today.

Today I want to revisit well-known feature of Java - we will take a look at Enums.

Let's start with a definition:

Enumeration - a collection of items that is a complete, ordered listing of all of the items in that collection.

[Wikipedia - Enumeration]


Sounds simple isn't it? Even if, Enums in Java allows us for many things and it's a really great language's feature, which can increase quality of our code.

Let's start with the basics and after this we will check how powerful Enums in Java really are.


type-safe

This mean your enum will have a type and you can not assign any value other than specified in Enum constants.

To be more specific, with following enum:

enum Season {
    SPRING, SUMMER, AUTUMN, WINTER
}

there is no problem to write something like:

Season favouriteSeason = Season.SUMMER;

but:

Season favouriteSeason = 1;

will end up with:

java.lang.Error: Unresolved compilation problem: 
    Type mismatch: cannot convert from int to Season


own namespace

Which means that even if values in one enumeration overlaps with other enum:

enum TDDStep {
    RED, GREEN, REFACTOR
}
enum TrafficLight {
    RED, ORANGE, GREEN 
}

Both are valid and can be used concurrently.


constants are implicitly static and final

It's impossible to do something like:

TDDStep.RED = TDDStep.GREEN;

It will just end up with:

java.lang.Error: Unresolved compilation problem: 
    The final field TDDStep.RED cannot be assigned


usable inside a switch statement.

So don't be afraid of writing something like this:

public void continueWork(TDDStep step) {
    switch (step) {
        case RED:
            makeTestGreen();
            break;

        case GREEN:
            refactoreCode();
            break;

        case REFACTOR:
            goToNextTest()
            break;
    }
}

It will work :) As well in code as in real life :)


can not create instance by using new

As it's written in documentation:

The constructor for an enum type must be package-private or private access. It automatically creates the constants that are defined at the beginning of the enum body. You cannot invoke an enum constructor yourself.

And this is (according to definition) because the Enum should be the only one responsible for returning the predefined instances.

To prevent any other modification:

The final clone method in Enum ensures that enum constants can never be cloned, and the special treatment by the serialization mechanism ensures that duplicate instances are never created as a result of deserialization. Reflective instantiation of enum types is prohibited. Together, these four things ensure that no instances of an enum type exist beyond those defined by the enum constants.

[JLS- Enums]

But if you would like to try anyway, you will get:

java.lang.Error: Unresolved compilation problem: 
    Illegal modifier for the enum constructor; only private is permitted.


what we get with no effort?

Take a look at simple enum:

public enum TDDStep {
    RED, GREEN, REFACTOR;
}


Not so impressive isn't it? But don't worry, even such simple construction gives as a few usefull methods:

@Test
public void stringToEnum() {
    assertEquals(TDDStep.RED, TDDStep.valueOf("RED"));
}

@Test
public void enumToString() {
    assertEquals("GREEN", TDDStep.GREEN.toString());
}

@Test
public void enumName() {
    assertEquals("REFACTOR", TDDStep.REFACTOR.name());
}

@Test
public void enumOrdinal() {
    assertEquals(1, TDDStep.GREEN.ordinal());
}

name() and ordinal() method are simple getters which returns values given in constructor which is emitted by the compiler in response to enum type declarations.

Constructor cannot be invoked by programmer and its declaration looks like this:

protected Enum(String name, int ordinal)


Method toString(), until is not overridden, will return the name of the enum constant, which is more less the same what name() is returning.


Method valueOf() looking for the enum which name is the same as a given value and returns it.


There is also one more method which is worth to mention:

@Test
public void valuesOfEnum() {
    TDDStep[] steps = {TDDStep.RED, TDDStep.GREEN, TDDStep.REFACTOR};

    assertArrayEquals(steps, TDDStep.values());
}


I believe that after looking at test there is no need for more explanation :)


In this paragraph I described the most important Enum's method which we just get because of using enum keyword.

Let's see what we can do more with it.


my own methods

Enumeration don't have to be just a simple list of an items. You can add your own method into it or/and provide your own constructor.

It's worth to mention that constructor have to be private and you cannot execute parent's (Enum) costructor, it would be done with no additional effort.


Category enum shows how to do this:

package com.smalaca.enumeration.designpatterns;

public enum Category {

    CREATIONAL("Deals with object creation mechanisms, trying to create objects in a manner suitable to the situation."),
    STRUCTURAL("Eases the design by identifying a simple way to realize relationships between entities."),
    BEHAVIORAL("Identifies common communication patterns between objects and realize these patterns.");

    private final String description;

    private Category(final String description) {
        this.description = description;
    }

    public String describe() {
        return description;
    }

    public String getExample() {

        String example = "";

        switch(this) 
        {
            case CREATIONAL:
                example = "Builder";
                break;

            case STRUCTURAL:
                example = "Adapter";
                break;

            case BEHAVIORAL:
                example = "Strategy";
                break;  
        }

        return example;
    }
}


Believe me or not, but it really works and if you want to verify it just run following tests:

@RunWith(Parameterized.class)
public class CategoryTest {

    private Category category;
    private String description;
    private String example;

    public CategoryTest(Category category, String description, String example) {
        this.category = category;
        this.description = description;
        this.example = example;
    }

    @Parameters
    public static Collection<Object[]> dataProvider() {
    return Arrays.asList(new Object[][] {
            { 
                Category.CREATIONAL, 
                "Deals with object creation mechanisms, trying to create objects in a manner suitable to the situation.",
                "Builder"
            },
            { 
                Category.STRUCTURAL, 
                "Eases the design by identifying a simple way to realize relationships between entities.",
                "Adapter"
            },
            { 
                Category.BEHAVIORAL, 
                "Identifies common communication patterns between objects and realize these patterns.",
                "Strategy"
            }
        });
    }

    @Test
    public void getCategoryExplanation()
    {
        assertSame(description, category.describe());
    }

    @Test
    public void getExample()
    {
        assertSame(example, category.getExample());
    }
}


If you are not familiar with @Parameterized annotation in JUnit look here for quick introduction.


gimme little bit of abstraction

Creating abstract methods is really useful from time to time that's why it's possible to do so in enumeration as well:

package com.smalaca.enumeration.designpatterns;

public enum Creational {

    ABSTRACT_FACTORY {
        @Override
        public String describe() {
            return "Provide an interface for creating families of related or dependent objects without specifying their concrete classes.";
        }
    },

    BUILDER {
        @Override
        public String describe() {
            return "Separate the construction of a complex object from its representation allowing the same construction process to create various representations.";
        }
    },

    FACTORY_METHOD {
        @Override
        public String describe() {
            return "Define an interface for creating a single object, but let subclasses decide which class to instantiate.";
        }
    };

    abstract public String describe();
}

Now run:

@RunWith(Parameterized.class)
public class CreationalTest {

    private Creational designPattern;
    private String description;

    public CreationalTest(Creational designPattern, String description) {
        this.designPattern = designPattern;
        this.description = description;
    }

    @Parameters
    public static Collection<Object[]> dataProvider() {
        return Arrays.asList(new Object[][] {
            { 
                Creational.ABSTRACT_FACTORY, 
                "Provide an interface for creating families of related or dependent objects without specifying their concrete classes.",
            },
            { 
                Creational.BUILDER, 
                "Separate the construction of a complex object from its representation allowing the same construction process to create various representations.",
            },
            { 
                Creational.FACTORY_METHOD, 
                "Define an interface for creating a single object, but let subclasses decide which class to instantiate.",
            }
        });
    }

    @Test
    public void getPatternExplanation()
    {
        assertSame(description, designPattern.describe());
    }
}


And? Everything is green, isn't it?


and what about interfaces?

We know how important interfaces are in well designed code. That's why it's great news for all of us that Enums allows for implementation of as many interfaces as you want to, just like regular class.


Let's assume that we have got following interface:

public interface HasDescription {
    public String describe();
}


Now we will change enums definition and add part about implementing newly added interface:

public enum Creational implements HasDescription { /** code */ )

and

public enum Category implements HasDescription { /** code */ )

Now run our tests once again. Is everything still ok? Should be :)


what about inheritance?

Ok, so it's possible to implement interfaces and create abstract methods and what about inheritance? 

I believe there is no need for any explanation at the moment, because I already mentioned something about parent in one of the paragraphs above, when I was writing about name() and ordinal() methods and constructor and calling parent in it, but to be exact...


As it's writen in tutorial:

All enums implicitly extend java.lang.Enum. Since Java does not support multiple inheritance, an enum cannot extend anything else.


an empty enum

As a curiosity I want to also write that is possible to create an empty enum and compiler will have no problem with it. Which means that enum below is valid:

public enum KnownAlienRace {}


As a side note, I want to tell you that I didn't use it ever so if you have got any good example which shows that creating enumeration like this have sense share with me in comments, I will appreciate :)



That's all for today.

I hope you enjoy it and learnt something new. Or at least refresh your memory :)

I'm waiting for your comments.


And... good luck with harnessing the power of Enums!

Find scaling and performance issues before your customers do with our Introduction to High-Capacity Load Testing guide.

Topics:
enum ,enumeration ,enums ,java ,oop ,ooad

Published at DZone with permission of Sebastian Malaca, DZone MVB. See the original article here.

Opinions expressed by DZone contributors are their own.

THE DZONE NEWSLETTER

Dev Resources & Solutions Straight to Your Inbox

Thanks for subscribing!

Awesome! Check your inbox to verify your email so you can start receiving the latest in tech news and resources.

X

{{ parent.title || parent.header.title}}

{{ parent.tldr }}

{{ parent.urlSource.name }}