Why Use Enums Instead of Constants? Which Is Better in Terms of Software Design and Readability

Why use Enums instead of Constants? Which is better in terms of software design and readability

Suppose you use constant strings (or int values - the same goes for them):

// Constants for player types
public static final String ARCHER = "Archer";
public static final String WARRIOR = "Warrior";

// Constants for genders
public static final String MALE = "Male";
public static final String FEMALE = "Female";

then you end up not really knowing the type of your data - leading to potentially incorrect code:

String playerType = Constants.MALE;

If you use enums, that would end up as:

// Compile-time error - incompatible types!
PlayerType playerType = Gender.MALE;

Likewise, enums give a restricted set of values:

String playerType = "Fred"; // Hang on, that's not one we know about...

vs

PlayerType playerType = "Fred"; // Nope, that doesn't work. Bang!

Additionally, enums in Java can have more information associated with them, and can also have behaviour. Much better all round.

Enums and Constants. Which to use when?

Use enums when you want to define a range of values that something can be. Colour is an obvious example like:

public enum Colour
{
White,
Red,
Blue
}

Or maybe a set of possible things like:
(Example I stole from here as I'm lazy)

[FlagsAttribute]
enum DistributedChannel
{
None = 0,
Transacted = 1,
Queued = 2,
Encrypted = 4,
Persisted = 16,
FaultTolerant = Transacted | Queued | Persisted
}

Constants should be for a single value, like PI. There isn't a range of PI values, there is just PI.

Other points to consider are:

  • a: Constants don't necessarily indicate a relationship between the constants, whereas an enumeration indicates that something can be one of the set defined by the enum.
  • b: A defined enumeration can help you with type checking when used as an argument. Constants are just values, so they don't provide any additional semantic information.

What are enums and why are they useful?

You should always use enums when a variable (especially a method parameter) can only take one out of a small set of possible values. Examples would be things like type constants (contract status: "permanent", "temp", "apprentice"), or flags ("execute now", "defer execution").

If you use enums instead of integers (or String codes), you increase compile-time checking and avoid errors from passing in invalid constants, and you document which values are legal to use.

BTW, overuse of enums might mean that your methods do too much (it's often better to have several separate methods, rather than one method that takes several flags which modify what it does), but if you have to use flags or type codes, enums are the way to go.

As an example, which is better?

/** Counts number of foobangs.
* @param type Type of foobangs to count. Can be 1=green foobangs,
* 2=wrinkled foobangs, 3=sweet foobangs, 0=all types.
* @return number of foobangs of type
*/
public int countFoobangs(int type)

versus

/** Types of foobangs. */
public enum FB_TYPE {
GREEN, WRINKLED, SWEET,
/** special type for all types combined */
ALL;
}

/** Counts number of foobangs.
* @param type Type of foobangs to count
* @return number of foobangs of type
*/
public int countFoobangs(FB_TYPE type)

A method call like:

int sweetFoobangCount = countFoobangs(3);

then becomes:

int sweetFoobangCount = countFoobangs(FB_TYPE.SWEET);

In the second example, it's immediately clear which types are allowed, docs and implementation cannot go out of sync, and the compiler can enforce this.
Also, an invalid call like

int sweetFoobangCount = countFoobangs(99);

is no longer possible.

Is it okay to create constant variables just for the sake of readability?

I believe constant is better than the magic number.

With constant, you control the definition in one place and better naming. It'll affect your further maintainability of the code.

And try using enum instead of constant in some situations. Enum has more pros than constant.

In this case enum example is similar to the below code:

enum UserInput {
NATIONAL(1), INTERNATIONAL(2), UNKNOWN(-1);

private int input;

public int getInput() {
return input;
}

UserInput(int i) {
this.input = i;
}

public static UserInput getUserInput(int input) {
for (UserInput userInput: UserInput.values()) {
if (userInput.getInput() == input) {
return userInput;
}
}
return UNKNOWN;
}
}

//main
public static void main(String[] args) {
System.out.println("Is the product:\n"+
"1. National.\n"+
"2. International.");
Scanner sc = new Scanner(System.in);
int choice = sc.nextInt();
switch (UserInput.getUserInput(choice)) {
case NATIONAL: break;
case INTERNATIONAL: break;
default:
}
}

check is for more: Why use Enums instead of Constants? Which is better in terms of software design and readability

Should I strictly avoid using enums on Android?

Use enum when you need its features. Don't avoid it strictly.

Java enum is more powerful, but if you don't need its features, use constants, they occupy less space and they can be primitive itself.

When to use enum:

  • type checking - you can accept only listed values, and they are not continuous (see below what I call continuous here)
  • method overloading - every enum constant has its own implementation of a method

    public enum UnitConverter{
    METERS{
    @Override
    public double toMiles(final double meters){
    return meters * 0.00062137D;
    }

    @Override
    public double toMeters(final double meters){
    return meters;
    }
    },
    MILES{
    @Override
    public double toMiles(final double miles){
    return miles;
    }

    @Override
    public double toMeters(final double miles){
    return miles / 0.00062137D;
    }
    };

    public abstract double toMiles(double unit);
    public abstract double toMeters(double unit);
    }
  • more data - your one constant contains more than one information that cannot be put in one variable

  • complicated data - your constant need methods to operate on the data

When not to use enum:

  • you can accept all values of one type, and your constants contain only these most used
  • you can accept continuous data

    public class Month{
    public static final int JANUARY = 1;
    public static final int FEBRUARY = 2;
    public static final int MARCH = 3;
    ...

    public static String getName(final int month){
    if(month <= 0 || month > 12){
    throw new IllegalArgumentException("Invalid month number: " + month);
    }

    ...
    }
    }
  • for names (like in your example)
  • for everything else that really doesn't need an enum

Enums occupy more space

  • a single reference to an enum constant occupies 4 bytes
  • every enum constant occupies space that is a sum of its fields' sizes aligned to 8 bytes + overhead of the object
  • the enum class itself occupies some space

Constants occupy less space

  • a constant doesn't have a reference so it's a pure data (even if it's a reference, then enum instance would be a reference to another reference)
  • constants may be added to an existing class - it's not necessary to add another class
  • constants may be inlined; it brings extended compile-time features (such as null checking, finding dead code etc.)

Which is better Java programming practice: stacking enums and enum constructors, or subclassing?

In the context you describe I would consider using a class hierarchy as opposed to enum definitions and supplementing this hierarchy with interfaces; e.g.

/**
* Root of class hierarchy.
*/
public interface InventoryItem {
}

/**
* Additional "parallel" interface implemented by some (but not all)
* InventoryItems and other non-inventory items.
*/
public interface Usable {
void use();
}

/**
* A Spell is in InventoryItem and is also Usable.
*/
public abstract class Spell implements InventoryItem, Usable {
}

public class Gremlin extends Spell {
}

/**
* A Door is *not* an InventoryItem but can be used.
*/
public class Door implements Usable {
}

The main advantage of this approach is that it allows you to treat a given object in different contexts (as InventoryItems or as a list of Usables). The other reason I would steer clear away from enums is that you're likely to define behaviour on your items (e.g. spell.cast(Person)) and this doesn't sit well in enums IMHO.

Preferring compile-time constants to enums in some specific cases

I think what is being discussed in that passage is the comparison between:

void myMethod(MyEnum enum){
switch(enum){
case VALUE1:
break;
case VALUE2:
break;
}
}

and

enum MyEnum{
VALUE1(){
protected void myMethod(){
//body
}
},
VALUE2(){
protected void myMethod(){
//body
}
}
protected abstract void myMethod();
}


Related Topics



Leave a reply



Submit