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.