CodeGym /Java Blog /Java Classes /Enum. Practical examples. Adding constructors and methods...
Author
Alex Vypirailenko
Java Developer at Toshiba Global Commerce Solutions

Enum. Practical examples. Adding constructors and methods

Published in the Java Classes group
Hi! Today we'll talk about one of Java's special data types: Enum (short for "enumeration"). What makes it special? Let's imagine what we need to implement "months" in a program. Enum. Practical examples. Adding constructors and methods - 1 Doesn't seem problematic, right? We just need to determine what properties that any month has. Perhaps we first need the name of the month and the number of days in it. The solution looks pretty simple:

public class Month {

   private String name;
   private int daysCount;

   public Month(String name, int daysCount) {
       this.name = name;
       this.daysCount = daysCount;
   }

   public String getName() {
       return name;
   }

   public void setName(String name) {
       this.name = name;
   }

   public int getDaysCount() {
       return daysCount;
   }

   public void setDaysCount(int daysCount) {
       this.daysCount = daysCount;
   }

   @Override
   public String toString() {
       return "Month{" +
               "name='" + name + '\'' +
               ", daysCount=" + daysCount +
               '}';
   }
}
The whole shabang! We have a Month class, the required fields, getter/setters, and toString(). Unless, of course, we need to add equals() and hashCode() to achieve complete happiness :) But here we have a conceptual problem. As you probably remember, one of the main advantages of OOP is that it makes it easy to model entities from the real world. A chair, a car, a planet — all these concepts from ordinary life are easily represented in a program with the help of abstraction. The problem is that some real-world entities have a strictly limited range of values. There are just 4 seasons in a year. There are just 8 notes in an octave. The calendar has just 12 months. And Danny Ocean of Ocean's 11 has just 11 friends (though this doesn't matter :)) What does matter is that an ordinary Java class is not able to model these entities and enforce their natural limitations. Our Month class has all the required fields. But if another programmer uses it, no one can stop him or her from creating completely insane objects:

public class Main {

   Month month1 = new Month("lolkek", 322);
   Month month2 = new Month("yahoooooooooooo", 12345);

}
If this appears in our code, it won't be easy to find the culprit! On the one hand, the programmer creating the objects might realize that the Month class means "month in a year" and not write such nonsense. On the other hand, the programmer is only taking advantage of abilities that the class designer provided. Is it possible to assign arbitrary names and numbers of days? That's exactly what we got. What then should we do in this situation? Honestly, before Java 1.5 was released, programmers had to get creative :) In those days, they created structures like this:

public class Month {

   private String name;
   private int daysCount;

   private Month(String name, int daysCount) {
       this.name = name;
       this.daysCount = daysCount;
   }

   public static Month JANUARY = new Month("January", 31);
   public static Month FEBRUARY = new Month("February", 28);
   public static Month MARCH = new Month("March", 31);

   @Override
   public String toString() {
       return "Month{" +
               "name='" + name + '\'' +
               ", daysCount=" + daysCount +
               '}';
   }
}
Here we have cut the number of months from twelve to three in order to make the example shorter. Such designs made it possible to solve the problem. The ability to create objects was limited to a private constructor:

private Month(String name, int daysCount) {
       this.name = name;
       this.daysCount = daysCount;
   }
Programmers using the class could not simply create Month objects. They had to use the final static objects provided by the class developer. For example, like this:

public class Main {

   public static void main(String[] args) {

       Month january = Month.JANUARY;
       System.out.println(january);
   }

}
But, Java developers drew attention to the existing problem. Of course, it's great that programmers were able to come up with a solution using the tools available in the language, but it doesn't look very easy! An obvious solution was needed, even for novices. And so Enum appeared in Java. Basically, Enum is a Java class that provides a limited set of object values. Here's how it looks:

public enum Month {
  
   JANUARY,
   FEBRUARY,
   MARCH
}
In the definition, we indicated that Enum is a Java class, but is that really true? Yes, and we can even verify it. For example, try making our Month enum inherit some other class:

public abstract class AbstractMonth {
}

// Error! The extends clause cannot be used with an enum
public enum Month extends AbstractMonth {

   JANUARY,
   FEBRUARY,
   MARCH
}
Why does that happen? When we write:

public enum Month
the compiler converts this statement into the following code:

public Class Month extends Enum
As you already know, Java doesn't support multiple inheritance. Therefore, we can't inherit AbstractMonth. How can this new construct, Enum, be used? And how does it differ from the old construct with static final fields? Well, as an example, the old construct did not let us use our own set of values in switch statements. Imagine that we want to create a program that will remind us of the holidays celebrated each month:

public class HolidayReminder {

   public void printHolidays(Month month) {

       switch (month) {

           // Error!
           case JANUARY:
       }
   }
}
As you can see, the compiler throws an error here. But once enum appeared in Java 1.5, everything became much simpler:

public enum Month {

   JANUARY,
   FEBRUARY,
   MARCH
}

public class HolidayReminder {

   public void printHolidays(Month month) {

       switch (month) {
          
           case JANUARY:
               System.out.println("New Year's Day is January 1st!");
               break;
           case FEBRUARY:
               System.out.println("Valentine's Day is February 14th!");
               break;
           case MARCH:
               System.out.println("Saint Patrick's Day is March 17th!");
               break;
       }
   }
}


public class Main {

   public static void main(String[] args) {

       HolidayReminder reminder = new HolidayReminder();
       reminder.printHolidays(Month.JANUARY);

   }

}
Console output:

New Year's Day is January 1st!
Note that access to Enum objects remained static, just as it was before Java 1.5. We don't need to create a Month object to access the months. When working with enums, it's very important not to forget that Enum is a full-fledged class. This means that, if necessary, you can define constructors and methods in it. For example, in the previous code fragment, we simply specified the values: JANUARY, FEBRUARY, MARCH. However, we can expand our Month enum like this:

public enum Month {

   JANUARY("January", 31),
   FEBRUARY("February", 28),
   MARCH("March", 31),
   APRIL("April", 30),
   MAY("May", 31),
   JUNE("June", 30),
   JULY("July", 31),
   AUGUST("August", 31),
   SEPTEMBER("September", 30),
   OCTOBER("October", 31),
   NOVEMBER("November", 30),
   DECEMBER("December", 31);

   private String name;
   private int daysCount;

   Month(String name, int daysCount) {
       this.name = name;
       this.daysCount = daysCount;
   }

   public static Month[] getWinterMonths() {

       return new Month[]{DECEMBER, JANUARY, FEBRUARY};
   }

   public static Month[] getSummerMonths() {

       return new Month[]{JUNE, JULY, AUGUST};
   }

   public String getName() {
       return name;
   }

   public void setName(String name) {
       this.name = name;
   }

   public int getDaysCount() {
       return daysCount;
   }

   public void setDaysCount(int daysCount) {
       this.daysCount = daysCount;
   }

   @Override
   public String toString() {
       return "Month{" +
               "name='" + name + '\'' +
               ", daysCount=" + daysCount +
               '}';
   }
}
Here we gave our enum 2 fields (the name of the month and the number of days), a constructor that uses these fields, getter/setters, the toString() method, and 2 static methods. As you can see, there were no problems with this. Again, Enum really is a full-fledged class:

import java.util.Arrays;

public class Main {

   public static void main(String[] args) {

       System.out.println(Arrays.toString(Month.getSummerMonths()));

   }

}
Console output:

[Month{name='June', daysCount=30}, Month{name='July', daysCount=31}, Month{name='August', daysCount=31}]
Finally, I want to recommend an extremely useful Java book, namely "Effective Java" by Joshua Bloch. Enum. Practical examples. Adding constructors and methods - 3The author is one of Java's creators, so you can definitely trust his advice on how to correctly and competently use language's tools :) In regards to our lesson, I recommend that you pay special attention to the book's chapter on Enum. Happy reading! :)
Comments (8)
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION
Jonaskinny Level 25, Redondo Beach, United States
20 February 2022
Best way to understand enum is this... public enum Month equates to ... public Class Month extends Enum <-- big E Enum which the JVM manages under the hood for us whenever we use the language keyword enum. JVM manages a ton of things for us that make Month instanceof Enum true.
Andrei Level 41
29 April 2021
Can someone please explain this bit? Why does the compiler throw and error when we try to use that month object in a switch statement?
SOUFIANE DAHIMI Level 32, Marrakech, Morocco
26 March 2020
Thankies , I was looking for such article