blogger templates blogger widgets
This is part of a list of blog posts.
To browse the contents go to

Java Enum(s) tutorial

Why use enums?

Checkout this answer from stackoverflow.

Start with a real time scenario of using enums:

package test;

import test.GradeTest.Grade;

public class GradeTest {
 
 public enum Grade { A, B, C, D, F, INCOMPLETE };
 
 public static void main(String[] args){
  
  Student student1 = new Student("John"); 
  Student student2 = new Student("Ben"); 

  student1.setGrade(Grade.B); 
  student2.setGrade(Grade.INCOMPLETE); 

  System.out.println(student1);
  System.out.println(student2);
  
 }
}

class Student { 
 
 private String name;
 private Grade grade;
 
 public Student(String name){
  this.name = name;
 }
 public void setGrade(Grade grade) {
  this.grade = grade;
 }
 public Grade getGrade() {
  return grade;
 }
 public void setName(String name) {
  this.name = name;
 }
 public String getName() {
  return name;
 }
 @Override
 public String toString() {
  return "Student: "+name+" got grade "+grade.toString();
 }
 
}

Properties of enums:

  1. enums are declared using enum keyword.

  2. enums extends java.lang.Enum.

  3. java.lang.Enum is an abstract class. This is the implicit base class for all enum types.
    It is declared as follows:
    public abstract class Enum extends Object implements Comparable, Serializable
    This clearly means that enums are comparable and serializable implicitly.

  4. Enumerated types aren't integers.
    Each declared value is an instance of the enum class itself; this ensures type-safety and allows
    for even more compile-time checking.

  5. Enums have no public constructor.

  6. Enum values are public, static, and final.

  7. The enum itself is effectively final, and so it cannot be subclassed.
    In fact, the specification says that you are not allowed to declare an enum as final or abstract,
    as the compiler will take care of those details.

  8. When declared inside a class (like the example above) it becomes a final static inner class.
    This explains why we needed to import test.GradeTest.Grade within the same program. (the same goes for inner classes).
    Also if you check the generated class files, you will notice that there is a GradeTest$Grade.class file.
    Note: Java doesnot have static (top-level) classes but has static inner classes (also known as static member classes).
    More here http://www.javaworld.com/article/2077372/learn-java/static-class-declarations.html

  9. As enum is static, you cannot access surrounding classes instance variables.

    If enum is defined outside the class.
    package test;
    
    enum Grade { A, B, C, D, F, INCOMPLETE };
    
    public class GradeTest {
     /*No changes*/
    }
    
    class Student { 
     /*No changes*/ 
    }
    
    Then we see that there is no need for the import. (Now after compiling, there is a Grade.class file)

  10. Enum values can be compared with == or equals().

  11. Enums override toString().
    The toString() method on an enumerated type returns the name of the value.
    Grade.A.toString() returns A.

  12. Enums provide valueOf() method.
    The final static valueOf() method internally calls toString().
    Grade.valueOf("A") returns A.

  13. Enums define a final instance method named ordinal().

    oridinal() returns the integer position of each enumerated value, starting at zero, based on
    the declaration order in the enum.

    enum Grade { 
     A, B, C, D, F, INCOMPLETE;
    
     public String toString() {
      return "Name of enum: "+this.name()+"; "+"Ordinal of enum: "+this.ordinal();
     }
    
    };
    
    public class GradeTest {
     
     public static void main(String[] args){
      
      System.out.println(Grade.A.toString());
      System.out.println(Grade.valueOf("A"));
     }
    }
    
    Name of enum: A; Ordinal of enum: 0
    Name of enum: A; Ordinal of enum: 0

  14. Enums define a values() method.
    values() return an array of the enum type. So, values() allows for iteration over the values of an enum.
    for(Grade grade : Grade.values()) {
     System.out.println(grade.name());
    }
    
    prints
    A
    B
    C
    D
    F
    INCOMPLETE

  15. Enum constructor

    By default, enums do not require you to give constructor definitions and
    their default values is always represented by string used in declaration.

    You MUST use private constructors when you override the default constructor.
    enum Grade { 
     A(5), B(4), C(3), D(2), F(1), INCOMPLETE(-1);
     
     private int gpa;
     
     private Grade(int gpa){
      this.gpa = gpa;
     }
    
     public int getGpa(){
      return gpa;
     }
    };
    
    Each enum constant corresponds to an enum object of that given enum type.

    In our example when the enum class Grade is initialized, the constructors are called and
    6 objects created.

  16. Any method added to an enum are implicitly static.
    enum Grade { 
     A(5), B(4), C(3), D(2), F(0), INCOMPLETE(-1);
     
     private int gpa;
     private String comment;
     
     private Grade(int gpa){
      this.gpa = gpa;
     }
    
     public int getGpa(){
      return gpa;
     }
    
     public String getComment() {
      return comment;
     }
    
     public void setComment(String comment) {
      this.comment = comment;
     }
    };
    
    System.out.println(Grade.A);
    Grade.A.setComment("You rock! Keep rocking!!");
    System.out.println(Grade.A.getComment());
    
    Grade grade1 = Grade.A;
    grade1.setComment("hihi");
    
    Grade grade2 = Grade.A;
    grade2.setComment("hoho");
    
    if(grade1==grade2){
     System.out.println("grade1==grade2");
    }
      
    if(grade1==Grade.A){
     System.out.println("grade1==Grade.A");
    }
    

  17. Enums work with switchs

    Prior to Java 1.4, switch only worked with int, short, char, and byte values.
    Grade grade = Grade.A;
     
    switch(grade) {
     case A:
      System.out.println("You got top grade");
      break;
     default :
      System.out.println("Die. The rest of you");
      break;
    }
    

  18. Maps of Enums

    http://docs.oracle.com/javase/7/docs/api/java/util/EnumMap.html

  19. Sets of Enums

    http://docs.oracle.com/javase/7/docs/api/java/util/EnumSet.html

  20. Interfaces with Enums
    interface GiftMachine {
     public String sendGift();
    }
    
    enum Grade implements GiftMachine{ 
     A(5), B(4), C(3), D(2), F(0), INCOMPLETE(-1);
     
     private int gpa;
    
     
     private Grade(int gpa){
      this.gpa = gpa;
     }
    
     public int getGpa(){
      return gpa;
     }
    
     @Override
     public String sendGift() {
      System.out.println("sending Gift");
      if(this.equals(Grade.A)){
       return "You get 10 million dollars";
      }
      return "boo... you get nothing";
     }
    };
    

  21. Value specific class bodies

    It means is that each enumerated value within a type can define value-specific methods.
    This cannot be done exclusively for a specific type.
    Instead the method is declared abstract for the enum and each type defines their own implementation.

    What happens is, 6 anonymous class definitions are created and instantiated.
    And now each type, say "A" now refers to this instance.

    If you check your class files, you will find 6 new files: Grade$1.class....Grade$6.class.
    enum Grade { 
     A(5){
      public void doSomething(){
       System.out.println("promote this guy");
      }
     }, B(4) {
      public void doSomething(){
       //don't do anything
      }
     }, C(3) {
      public void doSomething(){
       //don't do anything
      }
     }, D(2) {
      public void doSomething(){
       //don't do anything
      }
     }, F(0) {
      public void doSomething(){
       //don't do anything
      }
     }, INCOMPLETE(-1) {
      public void doSomething(){
       System.out.println("fail this guy");
      }
     };
     
     private int gpa;
     
     
     private Grade(int gpa){
      this.gpa = gpa;
     }
    
     public int getGpa(){
      return gpa;
     }
     
     public abstract void doSomething();
    
    };
    
    Grade grade = Grade.A;
    grade.doSomething();
    

  22. Manually define a custom enum

    You cannot do this.
    
    class MyEnum extends Enum { }
    
    Compiler stops you.

  23. Extending an enum

    You cannot do this.
    
    enum StudentGrade extends Grade { }
    
    Compiler stops you.

  24. Check if an object is an instance of enum or class - using Class.isEnum()
    
    Grade grade = Grade.A;
    System.out.println(grade.getClass().isEnum());
    

  25. Get all existing enum constants from a enum instance - using Class.getEnumConstants();
    
    Grade grade = Grade.A;
    Grade[] allgrades = grade.getClass().getEnumConstants();
    
    for(Grade g : allgrades) {
     System.out.println(g.name());
    }
    

  26. Why is enum declared in the API as follows

    public abstract class Enum<e extends Enum<E>>
    extends Object
    implements Comparable, Serializable
    

    Why is generics used here?

    A detailed answer is here

    But if you need a consise explanation here it is.
    abstract class Foo<subclassoffoo extends Foo<SubClassOfFoo>>
    {
        /** subclasses are forced to return themselves from this method */
        public abstract SubClassOfFoo subclassAwareDeepCopy();
    }
    
    class Bar extends Foo {
        public Bar subclassAwareDeepCopy() {
            Bar b = new Bar();
            // ...
            return b;
        }
    }
    
    Bar b = new Bar();
    Foo f = b;
    Bar b2 = b.subclassAwareDeepCopy();
    Bar b3 = f.subclassAwareDeepCopy(); // no need to cast, return type is Bar
    

    The trick going on here is:

    Any subclass of Foo must supply a type argument to Foo.
    That type argument must actually be a subclass of Foo.
    Subclasses of Foo (like Bar) follow the idiom that the type argument they supply to Foo is themselves.
    Foo has a method that returns SubClassOfFoo. Combined with the above idiom,
    this allows Foo to formulate a contract that says "any subclass of me must implement subclassAwareDeepCopy() and they
    must declare that it returns that actual subclass".

    Now,
    
    java.lang.Enum is declared as Enum<e extends Enum<E>>.
    
    E is used in the return type of getDeclaringClass(), and as an argument to compareTo().
    Which means you can write code like the following that
    a) doesn't need to cast and
    b) can use methods defined in Enum in terms of the concrete enum subclass.
    
    Rank r = Rank.ACE;
    Suit s = Suit.HEART;
    
    r.compareTo(s); // syntax error, argument must be of type Rank
    Rank z = Enum.valueOf(Rank.class, "TWO");
    


  27. Modify enums during runtime

    Older solution: niceideas.ch
    A little improved solution: javaspecialists

    Note that both solutions use a little bit of hacking using the sun.reflect package. These packages though included in the JDK is not accessible by default to the program. Reason being that these are used internally by JDK and are not part of the supported, public interface.

    Oracle explains it further.

    A Java program that directly calls into sun.* packages is not guaranteed to work on all Java-compatible platforms. In fact, such a program is not guaranteed to work even in future versions on the same platform.

    To make it work, if you are using eclipse then open Java build path -> Libraries -> Expand JRE System Library -> Double click Access rules -> Added access.



2 comments:

  1. Hi John,

    In example 25 we could just write

    Grade[] allgrades = Grade.class.getEnumConstants();

    to get the same result.

    ReplyDelete
  2. Point 12, "The final static valueOf() method internally calls toString()". This is incorrect, it calls name().

    ReplyDelete