Custom Enum

About

A Custom Enum is an enum that we define yourself in a project to fit our own specific needs. We can add fields, methods, and constructors to an enum to expand its capabilities, making it powerful and functional beyond just a list of constants.

Adding Fields and Methods to a Custom Enum

Custom enums can contain instance variables, constructors, and methods, enabling them to store and manipulate additional data.

Example of a Custom Enum with Fields and Methods

public enum ProductType {
    PREPAID_CARD("prepaid-card", "A prepaid card"),
    CREDIT_CARD("credit-card", "A credit card"),
    DEBIT_CARD("debit-card", "A debit card");

    private String code;
    private String description;

    ProductType(String code, String description) {
        this.code = code;
        this.description = description;
    }

    public String getCode() {
        return code;
    }

    public String getDescription() {
        return description;
    }

    public static ProductType fromCode(String code) {
        for (ProductType type : ProductType.values()) {
            if (type.code.equals(code)) {
                return type;
            }
        }
        throw new IllegalArgumentException("Unknown code: " + code);
    }
}

Usage of Custom Enum

In this example, ProductType stores a code and description for each constant

ProductType type = ProductType.fromCode("prepaid-card");
System.out.println(type.getDescription()); // Output: A prepaid card

Examples

Payment Confirmation Status

PaymentConfirmationStatus.java

package sample.enums;

import com.fasterxml.jackson.annotation.JsonCreator;
import com.fasterxml.jackson.annotation.JsonValue;

/**
 * The status of the confirmation.
 */
public enum PaymentConfirmationStatus {

    CONFIRMED("CONFIRMED"),

    PENDING("PENDING"),

    USER_DECLINED("USER DECLINED"),

    SYSTEM_DECLINED("SYSTEM DECLINED"),

    ERROR("ERROR");

    private String value;

    PaymentConfirmationStatus(String value) {
        this.value = value;
    }

    @JsonValue
    public String getValue() {
        return value;
    }

    @Override
    public String toString() {
        return String.valueOf(value);
    }

    @JsonCreator
    public static PaymentConfirmationStatus fromValue(String value) {
        for (PaymentConfirmationStatus b : PaymentConfirmationStatus.values()) {
            if (b.value.equals(value)) {
                return b;
            }
        }
        throw new IllegalArgumentException("Unexpected value '" + value + "'");
    }
}

Validation Enum

ValidationResult.java

package sample.enums;

import lombok.Getter;
import lombok.RequiredArgsConstructor;
import sample.model.ValidationResponse;

@Getter
@RequiredArgsConstructor
public enum ValidationResult {


    PASSED(Result.PASSED, "Provided value(s) are unique", "000"),
    REJECTED_NAME(Result.REJECTED, "The name is already used. Please use another", "001"),
    REJECTED_ID(Result.REJECTED, "Enter a valid ID number", "002");

    private final Result result;
    private final String message;
    private final String key;

    public ValidationResponse toHttpResponse() {
        return new ValidationResponse().result(result.name()).message(message).key(key);
    }

    public enum Result {
        PASSED, REJECTED
    }
}

Version Type

VersionType.java

package sample.enums;

import com.fasterxml.jackson.annotation.JsonCreator;
import com.fasterxml.jackson.annotation.JsonValue;

public enum VersionType {
  
    _1_0_0("1.0.0"),
    _2_0_0("2.0.0");

    private String value;

    VersionType(String value) {
        this.value = value;
    }

    @JsonValue
    public String getValue() {
        return value;
    }

    @Override
    public String toString() {
        return String.valueOf(value);
    }

    @JsonCreator
    public static VersionType fromValue(String value) {
        for (VersionType b : VersionType.values()) {
            if (b.value.equals(value)) {
                return b;
            }
        }
        throw new IllegalArgumentException("Unexpected value '" + value + "'");
    }
}

Yes or No Flag

YesNoFlagType.java

package sample.enums;

import com.fasterxml.jackson.annotation.JsonCreator;
import com.fasterxml.jackson.annotation.JsonValue;

/**
 * Flag indicating if a specific option is enabled or disabled with possible supported values. 
 */
public enum YesNoFlagType {
  
    Y("Y"),
    
    N("N");
  
    private String value;
  
    YesNoFlagType(String value) {
        this.value = value;
    }
  
    @JsonValue
    public String getValue() {
        return value;
    }
  
    @Override
    public String toString() {
        return String.valueOf(value);
    }
  
    @JsonCreator
    public static YesNoFlagType fromValue(String value) {
        for (YesNoFlagType b : YesNoFlagType.values()) {
            if (b.value.equals(value)) {
                return b;
            }
        }
        throw new IllegalArgumentException("Unexpected value '" + value + "'");
    }
}

Multi-stage order processing system

OrderState Enum: The OrderState enum defines multiple states for an order, such as RECEIVED, PROCESSING, SHIPPED, DELIVERED, and COMPLETED. Each enum constant has a stage number (int stage), which allows us to track the order's progress. Every state has a behavior defined in the handleOrder method. This method is abstract in the enum and is overridden in each state to handle the order’s transition logic. OrderState is a polymorphic enum. The method handleOrder is defined for each state, allowing us to change the order's state and behavior dynamically. The enum constants (RECEIVED, PROCESSING, etc.) override the abstract method to define their specific behavior. validTransitions is an EnumMap used to store valid transitions between states. For instance, an order can only go from RECEIVED to PROCESSING, from PROCESSING to SHIPPED, etc. The isTransitionValid static method checks if a state transition is allowed based on the current and next states.

OrderState.java

package sample;

import java.util.EnumMap;
import java.util.EnumSet;

public enum OrderState {
    RECEIVED(1) {
        @Override
        public void handleOrder(Order order) {
            System.out.println("Order " + order.getId() + " received.");
            order.setState(PROCESSING);
        }
    },
    PROCESSING(2) {
        @Override
        public void handleOrder(Order order) {
            System.out.println("Processing order " + order.getId() + "...");
            // Simulate some process time
            order.setState(SHIPPED);
        }
    },
    SHIPPED(3) {
        @Override
        public void handleOrder(Order order) {
            System.out.println("Order " + order.getId() + " shipped.");
            order.setState(DELIVERED);
        }
    },
    DELIVERED(4) {
        @Override
        public void handleOrder(Order order) {
            System.out.println("Order " + order.getId() + " delivered.");
            order.setState(COMPLETED);
        }
    },
    COMPLETED(5) {
        @Override
        public void handleOrder(Order order) {
            System.out.println("Order " + order.getId() + " completed.");
        }
    };

    private final int stage;

    // Constructor to initialize the enum with an additional field
    OrderState(int stage) {
        this.stage = stage;
    }

    public int getStage() {
        return stage;
    }

    // Abstract method that each state must implement
    public abstract void handleOrder(Order order);

    // Transition map to allow direct state transitions (for advanced use cases)
    private static final EnumMap<OrderState, EnumSet<OrderState>> validTransitions = new EnumMap<>(
        OrderState.class);

    static {
        validTransitions.put(RECEIVED, EnumSet.of(PROCESSING));
        validTransitions.put(PROCESSING, EnumSet.of(SHIPPED));
        validTransitions.put(SHIPPED, EnumSet.of(DELIVERED));
        validTransitions.put(DELIVERED, EnumSet.of(COMPLETED));
        validTransitions.put(COMPLETED, EnumSet.noneOf(OrderState.class));
    }

    public static boolean isTransitionValid(OrderState fromState, OrderState toState) {
        return validTransitions.get(fromState).contains(toState);
    }
}

Order Class: The Order class holds an OrderState and provides a method (setState) to change the state. The setState method ensures that only valid state transitions are allowed using isTransitionValid. If a transition is invalid (e.g., trying to go backward in the state machine), it prints a message and does not update the state.

Order.java

package sample;

import lombok.Getter;

@Getter
public class Order {
    private final String id;
    private OrderState state;

    public Order(String id) {
        this.id = id;
        this.state = OrderState.RECEIVED; // Initially in 'RECEIVED' state
    }

    public void setState(OrderState state) {
        if (OrderState.isTransitionValid(this.state, state)) {
            this.state = state;
            this.state.handleOrder(this);
        } else {
            System.out.println("Invalid state transition: " + this.state + " -> " + state);
        }
    }
}

OrderProcessingSystem.java

package sample;

public class OrderProcessingSystem {

    public static void main(String[] args) {
        // Create an order with ID 12345
        Order order = new Order("12345");

        // Print the current state of the order
        System.out.println("Initial Order State: " + order.getState());

        // Simulate state transitions using the handleOrder logic
        order.setState(OrderState.PROCESSING);  // Should transition to PROCESSING
        order.setState(OrderState.SHIPPED);     // Should transition to SHIPPED
        order.setState(OrderState.DELIVERED);   // Should transition to DELIVERED
        order.setState(OrderState.COMPLETED);   // Should transition to COMPLETED

        // Try invalid transition (e.g., from DELIVERED to PROCESSING)
        order.setState(OrderState.PROCESSING);  // Invalid transition from DELIVERED to PROCESSING
    }
}

Last updated

Was this helpful?