确保使用每个枚举值

时间:2015-03-12 11:30:10

标签: java enums

如果我使用enum来确定任务的类型。

public enum TaskType {
    TYPE_ONE("Type1"),TYPE_TWO("Type2"),TYPE_THREE("Type3");

    private final String type;

    private StageType(String type) {
        this.type = type;
    }

    @Override
    public String toString() {
        return type;
    }
}

我怎样才能确保我的申请中的某一点

if(taskType == TaskType.TYPE_ONE) {
    typeOneProcessing();
} else if(taskType == TaskType.TYPE_TWO) {
    typeTwoProcessing();
} else if(taskType == TaskType.TYPE_THREE) {
    typeThreeProcessing();
}

是否使用了每个enum值?
我的意思是如果有一天我需要添加一个新的TYPE_FOUR,我需要找到我使用enum的代码中的每个地方,所以我问自己是否有更好的方法以便我避免{ {3}}并使用其他一些概念,或者我可以确保在该段代码中使用enum的每个值。

6 个答案:

答案 0 :(得分:5)

有findbugs类型工具可以做到这一点,但你可以考虑完全删除if-then-else并将处理放在enum内。在这里,添加新的TYPE_FOUR会强制您编写doProcessing()方法。

public interface DoesProcessing {

    public void doProcessing();
}

public enum TaskType implements DoesProcessing {

    TYPE_ONE("Type1") {
                @Override
                public void doProcessing() {

                }
            },
    TYPE_TWO("Type2") {
                @Override
                public void doProcessing() {

                }
            },
    TYPE_THREE("Type3") {
                @Override
                public void doProcessing() {

                }
            },
    TYPE_FOUR("Type4") {
        // error: <anonymous com.oldcurmudgeon.test.Test$TaskType$4> is not abstract and does not override abstract method doProcessing() in DoesProcessing
            };

    private final String type;

    private TaskType(String type) {
        this.type = type;
    }

    @Override
    public String toString() {
        return type;
    }
}

public void test() {
    DoesProcessing type = TaskType.TYPE_TWO;
    type.doProcessing();
}

如果您更喜欢abstract方法,那么这可行:

public enum TaskType {

    TYPE_ONE("Type1") {
                @Override
                public void doProcessing() {

                }
            },
    TYPE_TWO("Type2") {
                @Override
                public void doProcessing() {

                }
            },
    TYPE_THREE("Type3") {
                @Override
                public void doProcessing() {

                }
            };

    private final String type;

    private TaskType(String type) {
        this.type = type;
    }

    // Force them all to implement doProcessing.
    public abstract void doProcessing();

    @Override
    public String toString() {
        return type;
    }
}

答案 1 :(得分:2)

您可以将进程方法作为抽象方法放在TaskType中,然后在枚举中的每个任务中覆盖它。如果您创建一个界面,可能是一个更好的主意,例如:

public interface Task {
    void process();
}

然后你要让你的枚举实现这个界面。或者,可能更好,您创建实现此接口的具体类。每个任务类型都有一个类。

答案 2 :(得分:1)

AFAIK你不能自动&#34;。

为了尽量减少忘记为新值添加if / case的风险,您可以拥有一个&#34; service&#34;每个枚举值的类和为枚举值提供特定服务的工厂。

E.g。而不是:

void methodA(TaskType type) {
   doSth();
   switch(type) {
      case TYPE_ONE:
        foo1(); 
        break;
      case TYPE_TWO:
        foo2();
        break;
      ...
   }
}
void methodB(TaskType type) {
   doSthElse();
   switch(type) {
      case TYPE_ONE:
        bar1(); 
        break;
      case TYPE_TWO:
        bar2();
        break;
      ...
   }
}

做的:

interface Service {
   foo();
   bar();
}
class ServiceFactory {
   Service getInstance(TaskType type) {
      switch(type) {
         case TYPE_ONE:
            return new TypeOneService();
         case TYPE_TWO:
            return new TypeTwoService();
         default:
            throw new IllegalArgumentException("Unsupported TaskType: " + type);
      }
   }
}

然后上面的方法可以改写如下:

void methodX(TaskType type) {
   doSth();
   ServiceFactory.getInstance(type).foo();
}

这样你只需要一个点来添加新枚举值的处理。

答案 3 :(得分:1)

我想你是说你想要编译器告诉你所有的枚举值都被考虑了。

不幸的是,Java并不支持。

你可能认为你可以这样写:

public int method(TaskType t) {
    switch (t) {
    case TYPE_ONE: return 1;
    case TYPE_TWO: return 2;
    case TYPE_THREE: return 3;
    }
    // not reachable ... no return required
}

...并依赖编译器告诉您是否遗漏了切换案例中的一个枚举值。

不幸的是,它不起作用!! 以上是编译错误。根据JLS可达性规则,switch语句需要default:臂才能使该方法有效。 (或者你可以在最后添加return ...)

这种奇怪的原因很有道理。 JLS二进制兼容性规则表示向enum添加新值是二进制兼容的更改。这意味着在添加枚举值之后,任何带有switch语句的开启enum的代码仍需要保留有效(可执行)代码。如果method开头有效,则在二进制兼容更改后,它将无效(因为返回路径中没有return语句)。


实际上,这就是我编写上面代码的方式:

public int method(TaskType t) {
    switch (t) {
    case TYPE_ONE: return 1;
    case TYPE_TWO: return 2;
    case TYPE_THREE: return 3;
    default:
       throw new AssertionError("TaskType " + t + " not implemented");
    }
    // not reachable ... no return required
}

这并不是假装编译时安全的,但它是快速失败的,并且它不会涉及糟糕的OO设计。

答案 4 :(得分:0)

HashMap<String, Integer> hm=new HashMap<String, Integer>();

...

if(taskType == TaskType.TYPE_ONE) {
    typeOneProcessing();
    hm.put(TaskType.TYPE_ONE, 1)
} else if(taskType == TaskType.TYPE_TWO) {
    typeTwoProcessing();
    hm.put(TaskType.TYPE_TWO, 1)
} else if(taskType == TaskType.TYPE_THREE) {
    typeThreeProcessing();
    hm.put(TaskType.TYPE_THREE, 1)
}

...

for (TaskType t : TaskType.values()) {
  if(hm.get(t)!=1)
     // Trigger the alarm
}

如果需要,您甚至可以计算元素计数的次数

答案 5 :(得分:0)

您可以在枚举上切换案例,如果达到默认值,则会失败:

switch(taskType ){
  case TYPE_ONE: ... break;
  case TYPE_TWO: ... break;
  case TYPE_THREE: ... break;
  default: 
     throw new IllegalStateException("Unsupported task type:"+taskType);
  }