Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Nested enums in Java?

Tags:

java

enums

I want to define some enums for the various ajax services that I have available in my web application, like:

Enum Service
{
    REGISTER,
    LOGIN,
    NEWS,
    FAQ
}

However, each of these enums will also have a certain state like Failed, Loaded, etc. So I want to be able to use REGISTER.LOADED, LOGIN.LOADED etc, to fire up events on my event bus. However each state enum must be unique. I.e Register.LOADED must be different from FAQ.LOADED, and so on.

Edit: Also, I must be able to store all states in the same hashmap, e.g Register.LOADED and Login.LOADED must be storable in the same hashmap. And the parent service enums, i.eLOGIN, REGISTER etc must be storable in the same hashmap as well.

What's the best way to accomplish this?

like image 777
Ali Avatar asked Feb 14 '14 15:02

Ali


3 Answers

After some comments from the OP, I've updated my code. The usage syntax gets ugly, and it may not work particularly well in switch blocks, but I believe this achieves what the OP is looking for:

import java.util.*;

public class HelloWorld
{
    public static void main(String []args)
    {
        // Different services are not equal
        System.out.println(Service.REGISTER + "==" + Service.LOGIN + " :: " + Service.REGISTER.equals(Service.LOGIN));
        // Same service is equal
        System.out.println(Service.REGISTER + "==" + Service.REGISTER + " :: " + Service.REGISTER.equals(Service.REGISTER));
        // Even after changing the state
        Service tmp = Service.REGISTER;
        Service.REGISTER.setState(Service.ServiceState.State.FAILED);
        System.out.println(Service.REGISTER + "==" + tmp + " :: " + Service.REGISTER.equals(tmp));
        
        Service.REGISTER.setState(Service.ServiceState.State.LOADED);
        
        // Different service, same state is not equal
        System.out.println(Service.REGISTER + "." + Service.REGISTER.state + "==" + Service.LOGIN + "." + Service.LOGIN.state + " :: " + Service.REGISTER.state.equals(Service.LOGIN.state));
        
        // Same service, same state is equal (even when changing state around)
        Service.ServiceState.State temp = Service.REGISTER.getState();
        Service.REGISTER.setState(Service.ServiceState.State.LOADED);
        System.out.println(Service.REGISTER + "." + Service.REGISTER.state + "==" + tmp + "." + temp + " :: " + temp.equals(Service.REGISTER.getState()));
        
        // Same service, different state is not equal
        Service.REGISTER.setState(Service.ServiceState.State.FAILED);
        System.out.println(Service.REGISTER + "." + Service.REGISTER.state + "==" + tmp + "." + temp + " :: " + temp.equals(Service.REGISTER.getState()));
        
        // Both service and state can be used as map keys
        Map<Service, String> map = new HashMap<Service, String>();
        Map<Service.ServiceState.State, String> map2 = new HashMap<Service.ServiceState.State, String>();
    }
}

enum Service
{
    REGISTER(),
    LOGIN(),
    NEWS(),
    FAQ();

    public ServiceState state;

    Service()
    {
        this.state = new ServiceState();
    }
    
    public void setState(ServiceState.State s)
    {
        state.state = s;
    }
    
    public ServiceState.State getState() { return state.state; }

    public static class ServiceState
    {
        public enum State
        {
            LOADED,
            FAILED
        }
        
        public State state = State.LOADED;
        
        public ServiceState(){}
        
        public String toString() { return state.toString(); }
        
        public int hashCode()
        {
            return state.hashCode();
        }
        
        public boolean equals(Object obj)
        {
            return state.equals(obj);
        }
    }
}

Output:

REGISTER==LOGIN :: false
REGISTER==REGISTER :: true
REGISTER==REGISTER :: true
REGISTER.LOADED==LOGIN.LOADED :: false
REGISTER.LOADED==REGISTER.LOADED :: true
REGISTER.FAILED==REGISTER.LOADED :: false

Original Answer:

Not quite possible with the syntax you want, but Java enums are basically just classes by another name. (In fact, once compiled, they are classes which extend java.lang.Enum<E extends Enum<E>>)

public class HelloWorld
{
    public static void main(String []args)
    {
        Service a = Service.REGISTER;
        a.state = Service.ServiceState.LOADED;
        System.out.println(a);
    }
}

enum Service
{
    REGISTER(),
    LOGIN(),
    NEWS(),
    FAQ();
    
    public ServiceState state;
    
    Service()
    {
        this.state = ServiceState.LOADED;
    }
    
    public String toString()
    {
        return super.toString() + "." + state.toString();
    }
    
    public enum ServiceState
    {
        LOADED,
        FAILED
    }
}

Output:

REGISTER.LOADED

like image 60
Brian S Avatar answered Oct 27 '22 03:10

Brian S


Another option is to create several enums. One lists the services:

enum Service {
    REGISTER, LOGIN, NEWS, FAQ;
}

the next one lists all states:

enum State { LOADED, FAILED }

Now you can either have a class which takes a service and a state in the constructor and which overrides equals() and hashCode() to distinguish them:

new ServiceState(REGISTER, LOADED)

or you create another enum which list valid combinations:

enum ServiceState {
    REGISTER_LOADED(REGISTER, LOADED), ...
}

but this enum will be very big and the amount to type will soon become very big (if you add one value to the other enums, you need to add N enums to ServiceState)

like image 29
Aaron Digulla Avatar answered Oct 27 '22 02:10

Aaron Digulla


Just turn the "external" enum into a class and fill it with "internal" enums:

public class Service {
    public enum REGISTER {
        CANCELLED
    }

    // ...

    public enum LOGIN {
        LOADED
    }
}

or nest it as public static into the handler class, if it is the case.

like image 23
Stefano Sanfilippo Avatar answered Oct 27 '22 04:10

Stefano Sanfilippo