Java中的嵌套枚举?

时间:2014-02-14 15:18:22

标签: java enums

我想为我在Web应用程序中提供的各种ajax服务定义一些枚举,例如:

Enum Service
{
    REGISTER,
    LOGIN,
    NEWS,
    FAQ
}

但是,这些枚举中的每一个都会有一个像Failed,Loaded等一样的状态。所以我希望能够使用REGISTER.LOADEDLOGIN.LOADED等来启动我的活动事件总线。但是每个状态枚举必须是唯一的。 I.e Register.LOADED必须与FAQ.LOADED不同,依此类推。

编辑:此外,我必须能够将所有状态存储在同一个散列映射中,例如Register.LOADEDLogin.LOADED必须可以存储在同一个散列映射中。父服务枚举,即LOGIN, REGISTER等,也必须存储在同一个散列映射中。

实现这一目标的最佳方法是什么?

5 个答案:

答案 0 :(得分:7)

在OP的一些评论之后,我已经更新了我的代码。使用语法变得丑陋,在switch块中可能无法正常工作,但我相信这可以实现OP的目标:

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);
        }
    }
}

输出:

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

原始答案:

使用您想要的语法不太可能,但Java枚举基本上只是另一个名称的类。 (事实上​​,一旦编译,它们是扩展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
    }
}

输出:

  

REGISTER.LOADED

答案 1 :(得分:2)

另一种选择是创建几个枚举。一个列出了服务:

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

下一个列出所有州:

enum State { LOADED, FAILED }

现在你可以拥有一个在构造函数中获取服务和状态的类,它会覆盖equals()和hashCode()来区分它们:

new ServiceState(REGISTER, LOADED)

或者您创建另一个列出有效组合的枚举:

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

但是这个枚举非常大,输入的数量很快会变得非常大(如果你向其他枚举添加一个值,你需要将N个枚举添加到ServiceState

答案 2 :(得分:1)

一种解决方案是

enum Service {
  REGISTER, LOGIN, NEWS, FAQ;

  public final class State {
    final ServiceState state;
    private State(ServiceState st) {
      state=st;
    }
    @Override
    public String toString() {
      return Service.this+"."+state;
    }
  }
  final EnumMap<ServiceState, State> map=new EnumMap<>(ServiceState.class);
  Service() {
    for(ServiceState s:ServiceState.values()) map.put(s,new State(s));
  }
  public State state(ServiceState s) { return map.get(s); }
}
enum ServiceState { LOADED, FAILED }

这具有自动适应可能ServiceState的更改的吸引力,但使用它们的代码有点奇怪:

Service.State ss=Service.LOGIN.state(ServiceState.LOADED); // LOGIN.LOADED

另一种选择是:

enum Service {
  REGISTER, LOGIN, NEWS, FAQ;

  public final class State {
    final ServiceState state;
    private State(ServiceState st) {
      state=st;
    }
    @Override
    public String toString() {
      return Service.this+"."+state;
    }
  }
  public final State LOADED=new State(ServiceState.LOADED);
  public final State FAILED=new State(ServiceState.FAILED);
}
enum ServiceState { LOADED, FAILED }

这需要在您更改可能的ServiceState时手动更新字段,但在使用它时它具有更吸引人的语法:

Service.State ss=Service.LOGIN.LOADED;

您还可以将ServiceState enum String "LOADED""FAILED"等替换为enum Service,这样您就不需要保留{ {1}}和Service.State内的字段同步。


请注意,这两个解决方案都依赖于private的构造函数为enum的事实,因此只存在equals中存在的规范实例。因此,您无需覆盖hashCodeSerializable。如果你决定创建类readResolve,你还应该添加一个{{1}}方法,该方法返回反序列化实例的规范表示。

答案 3 :(得分:0)

只需将“外部”枚举变成一个类,然后用“内部”枚举填充它:

public class Service {
    public enum REGISTER {
        CANCELLED
    }

    // ...

    public enum LOGIN {
        LOADED
    }
}

或将其作为public static嵌套到处理程序类中(如果是这种情况。

答案 4 :(得分:0)

虽然有一些好的答案可能适用于其他情况,但我决定采用以下解决方案,因为它最适合我的情况:

public enum Service
{
    LOGIN,
    REGISTER,
    NEWS;

    private final long LOADED, FAILED;

    private RequestType()
    {
        LOADED  = strToAscii(this.name() + "_LOADED"  );
        FAILED = strToAscii(this.name() + "_FAILED"  );            
    }

    public static long strToAscii(String str)
    {
       StringBuilder sb = new StringBuilder();

       for (char c: str.toCharArray() )
       {
           sb.append( (int) c );
       }

      return Long.parseLong( sb.toString() );
   }
}

这对我来说效果很好,因为我现在可以像我原先想要的那样RequestType.LOGIN.LOADED == RequestType.REGISTER.LOADED。如果有人有任何改进建议,请随时提出建议。