JMU
Class-Enum Hybrids
An Introduction with Examples in Java


Prof. David Bernstein
James Madison University

Computer Science Department
bernstdh@jmu.edu


Motivation
Motivation (cont.)
An Example of the Less-Common Situation
Properties of the Enumerated Instances
Comparing Instances
An Example (cont.)
javaexamples/oopbasics/Temperature.java
        /**
 * An encapsulation of a very simple immutable Temperature class/enum
 * hybrid.
 *
 * Note: For simplicity, Temperature objects do not have an associated
 * scale (e.g., Farenheit, Celsius/Centigrade, or Kelvin).
 *
 * @author  Prof. David Bernstein, James Madison University
 * @version 1.0
 */
public class Temperature
{
    private double    value;
    private String    stringValue;
    
    public static final Temperature    OFF = new Temperature();    

    /**
     * Construct a Temperature object with a stringValue of "Off"
     */
    private Temperature()
    {
       stringValue = "Off   ";
    }

    /**
     * Explicit Value Constructor.
     *
     * @param value   The value of this Temperature
     */
    public Temperature(double value)
    {
       this.value = value;

       // Since objects in this class are immutable, it makes sense to
       // create the String representation now
       stringValue = String.format("%-6.1f", this.value);
    }

    /**
     * Return true if and only if this Temperature objects
     * has the same value as the given Temperature object.
     *
     * @param other   The Temperature of interest
     */
    public boolean equals(Temperature other)
    {
        return stringValue.equals(other.stringValue);
    }
    
    /**
     * Return a String representation of this Temperature
     *
     * @return   The String representation
     */
    public String toString()
    {
       return stringValue;       
    }
}
        
The equals() Method Revisited

An Implementation that Works in a Wider Variety of Situations

    public boolean equals(Temperature other)
    {
      if       ((other == OFF) && (this == OFF)) return true;
      else if  ((other == OFF) && (this != OFF)) return false;
      else if  ((other != OFF) && (this == OFF)) return false;
      else     return stringValue.equals(other.stringValue);
    }
  
Something to Think About
Something to Think About (cont.)

The Month Enum as a Class

javaexamples/oopbasics/months/class/Month.java (Fragment: 0)
        /**
 * A class (rather than an enum) for the months of the year.
 *
 * @author  Prof. David Bernstein, James Madison University
 */
public class Month
{
    public static final Month JANUARY   = new Month("January",  31);
    public static final Month FEBRUARY  = new Month("February", 28);
    public static final Month MARCH     = new Month("March",    31);
    public static final Month APRIL     = new Month("April",    30);    
    public static final Month MAY       = new Month("May",      31);
    public static final Month JUNE      = new Month("June",     30);
    public static final Month JULY      = new Month("July",     31);    
    public static final Month AUGUST    = new Month("August",   31);
    public static final Month SEPTEMBER = new Month("September",30);
    public static final Month OCTOBER   = new Month("October",  31);
    public static final Month NOVEMBER  = new Month("November", 30);
    public static final Month DECEMBER  = new Month("December", 31);

    private final int    days;
    private final String name;

    /**
     * Explicit Value Constructor.
     *
     * @param name   The name of the Month
     * @param days   The number of days in the Month
     */
    private Month(String name, int days)
    {
        this.name = name;
        this.days = days;
    }
    /**
     * Get the 3-letter abbreviation for this Month.
     *
     * @return  The 3-letter abbreviation
     */
    public String getAbbreviation()
    {
        String       result;
      
        if (name.length() > 3) result = name.substring(0,3)+".";
        else                   result = name;

        return result;      
    }

    /**
     * Get the numeric value of this Month (in the interval [1,12]).
     *
     * @return  The numeric value of this Month
     */
    public int getNumber()
    {
        return ordinal()+1; // ordinal() returns the position in the declaration
    }

    /**
     * Get the (normal) number of days in this Month.
     *
     * @return  The normal number of days in this Month.
     */
    public int getLength()
    {
        return days;      
    }
    /**
     * Return the Month that corresponds to a given String.
     *
     * @param s   The String representation
     * @return    The corresponding Month
     * @throws    IllegalArgumentException if there is no match
     */
    public Month parseMonth(String s) throws IllegalArgumentException
    {
        Month   result;
        String  u;
       
        u = s.toUpperCase();
       
        result = Month.valueOf(u);           
        return result;

       
        // A more elaborate version
        //
        // Month[] all;
        // all = Month.values();
        // for (int i=0; i<all.length; i++)
        // {
        //    if (s.equalsIgnoreCase(all[i].name) 
        //        || s.equalsIgnoreCase(all[i].getAbbreviation()))
        //    {
        //        return all[i];
        //    }
        // }
        // throw new IllegalArgumentException("No such Month");
    }
   
    /**
     * Get a String representation of this Month
     *
     * @return   The String representation (i.e., the name)
     */
    public String toString()
    {
        return name;      
    }
}
        
Something to Think About (cont.)
Something to Think About (cont.)

The Improved Month Class

javaexamples/oopbasics/months/class/Month.java
        /**
 * A class (rather than an enum) for the months of the year.
 *
 * @author  Prof. David Bernstein, James Madison University
 */
public class Month
{
    private static final Month[]  VALUES = new Month[12];

    private static       int      next   = 0;    

    private static final String[] IDS = 
    {
        "JANUARY", "FEBRUARY", "MARCH", "APRIL", "MAY", "JUNE",
        "JULY", "AUGUST", "SEPTEMBER", "OCTOBER", "NOVEMBER", "DECEMBER"
    };

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

    private final int    days;
    private final String name;

    /**
     * Explicit Value Constructor.
     *
     * @param name   The name of the Month
     * @param days   The number of days in the Month
     */
    private Month(String name, int days)
    {
        this.name = name;
        this.days = days;
        VALUES[next] = this;
        ++next;
    }
    /**
     * Compare this Month to the given month.
     *
     * @param other  The Month of interest
     * @return       -1, 0, or 1
     */
    public int compareTo(Month other)
    {
        if       (this.ordinal() < other.ordinal())  return -1;
        else if  (this.ordinal() == other.ordinal()) return  0;
        else                                         return  1;
    }
    /**
     * Get the 3-letter abbreviation for this Month.
     *
     * @return  The 3-letter abbreviation
     */
    public String getAbbreviation()
    {
        String       result;
      
        if (name.length() > 3) result = name.substring(0,3)+".";
        else                   result = name;

        return result;      
    }

    /**
     * Get the numeric value of this Month (in the interval [1,12]).
     *
     * @return  The numeric value of this Month
     */
    public int getNumber()
    {
        return ordinal()+1; // ordinal() returns the position in the declaration
    }

    /**
     * Get the (normal) number of days in this Month.
     *
     * @return  The normal number of days in this Month.
     */
    public int getLength()
    {
        return days;      
    }
    /**
     * Return the ordinal value of this instance.
     *
     * @return  The ordinal value (0-based)
     */
    public int ordinal()
    {
        for (int i=0; i<VALUES.length; i++)
        {
            if (this == VALUES[i]) return i;            
        }
        // Shouldn't get here!
        return -1;
    }
    /**
     * Return the Month that corresponds to a given String.
     *
     * @param s   The String representation
     * @return    The corresponding Month
     * @throws    IllegalArgumentException if there is no match
     */
    public Month parseMonth(String s) throws IllegalArgumentException
    {
        Month   result;
        String  u;
       
        u = s.toUpperCase();
       
        result = Month.valueOf(u);           
        return result;

       
        // A more elaborate version
        //
        // Month[] all;
        // all = Month.values();
        // for (int i=0; i<all.length; i++)
        // {
        //    if (s.equalsIgnoreCase(all[i].name) 
        //        || s.equalsIgnoreCase(all[i].getAbbreviation()))
        //    {
        //        return all[i];
        //    }
        // }
        // throw new IllegalArgumentException("No such Month");
    }
   
    /**
     * Get a String representation of this Month
     *
     * @return   The String representation (i.e., the name)
     */
    public String toString()
    {
        return name;      
    }
    /**
     * Get all of the Month objects.
     *
     * @return An array containing all instances
     */
    public static Month[] values()
    {
        return VALUES;
    }

    /**
     * Get the Month associated with a particaulr identifier.
     *
     * @param s  The identifier
     * @return   The corresponding Month (or null)
     */
    public static Month valueOf(String s)
    {
        for (int i=0; i<IDS.length; i++)
        {
            if (s.equals(IDS[i])) return VALUES[i];
        }
        return null;
    }
}
        
Something to Think About (cont.)