Favour the Typesafe Enum pattern in Apex

A frequent need is to have behaviour that varies according to a value such as picklist String value. The simplest approach is to pass that value around as a String and to compare the value against inline String constants or static final Strings declared somewhere. But weaknesses of this approach are:

  • Not typesafe – other values can be accidentally introduced
  • Not self-documenting – using the type String says nothing about the domain/purpose
  • Any related attributes or logic has to be added inline or in a separate class using if/elseif/else chains

(Java had the enum types language feature added in version 5 to address these problems; Apex’s enum language feature is basic in comparison.)

A pattern that addresses these problems and was often recommended in Java before enum types were added is the “typesafe enum” pattern. Here is an Apex example:

public class Status {

    private static final Map<String, Status> STATUSES = new Map<String, Status>();
    
    public static final Status PENDING = new Status('Pending', 5);
    public static final Status OPEN = new Status('Open', 30);
    public static final Status CLOSED = new Status('Closed', 60);
    
    public static Status valueOf(String name) {
        return STATUSES.get(name);
    }
    
    public static Status[] values() {
        return STATUSES.values();
    }
    
    public String name {get; private set;}
    public Integer days {get; private set;}
    
    private Status(String name, Integer days) {
        this.name = name;
        this.days = days;
        STATUSES.put(name, this);
    }
    
    public override String toString() {
        return name;
    }
     
    public Boolean equals(Object o) {
        if (o instanceof Status) {
            Status that = ((Status) o);
            return this.name == that.name;
        }
        return false;
    }
    
    public Integer hashCode() {
        return name.hashCode();
    }
}

Code using it looks like this:

    private void method1() {
        ...
        method2('xyz', Status.OPEN);
        ...
    }
    
    private void method2(String v, Status s) {
        ...
        Date d = Date.now().addDays(s.days);
        String message = 'Status is ' + s;
        ...
    }

To convert a picklist value (or other String) to an instance of this object:

Status status = Status.valueOf(picklistValue);

To iterate over all the values:

for (Status status : Status.values()) {
    ...
}

Extra methods can be added and so can extra data values.

Note that the equals/hashCode methods are only needed if references are serialized. Examples of where that can happen are in Visualforce’s view state or when Database.Stateful is used in a Batchable.

PS

This approach can be used for run-time loaded values (e.g. using describe calls on picklist fields or loading from JSON) though that of course means not having constants for each value. If that is done I strongly recommend lazy loading to avoid filling the debug log with entries for cases where the full set of values are not needed.

Advertisements

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s