确保使用每个枚举值
Ensure every enum value is used
如果我使用 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 的每个地方,所以我问自己是否有更好的方法,以便我要么避免 enum 并使用其他概念,要么我可以确保 enum 的每个值都用于该段代码。
有 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> 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;
}
}
您可以将处理方法作为抽象方法放在 TaskType 中,然后在枚举中的每个任务中重写它。如果您创建一个界面,可能是一个更好的主意,例如:
public interface Task {
void process();
}
然后你要么让你的枚举实现这个接口。或者,可能更好的是,您创建实现此接口的具体 classes。每个任务类型 class。
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
}
如果需要,您甚至可以计算元素被计数的次数
您可以在枚举上做 switch case,如果命中默认值则失败:
switch(taskType ){
case TYPE_ONE: ... break;
case TYPE_TWO: ... break;
case TYPE_THREE: ... break;
default:
throw new IllegalStateException("Unsupported task type:"+taskType);
}
据我所知你做不到 "automatically"。
为了尽量减少忘记为新值添加 if/case 的风险,您可以为每个枚举值添加一个 "service" class 和一个为枚举提供特定服务的工厂值。
例如而不是:
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();
}
这样一来,您只有一处必须添加新枚举值的处理。
我想你是说你希望编译器告诉你所有枚举的值都被考虑了。
很遗憾,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
}
... 并依靠编译器告诉您是否遗漏了 switch 案例中的枚举值之一。
可惜不行!!以上反正是编译错误。根据 JLS 可达性规则,switch 语句需要 default:
分支才能使该方法有效。 (或者你可以在最后加一个return
...)
这种奇怪现象是有充分理由的。 JLS 二进制兼容性规则指出,向 enum
添加新值是二进制兼容更改。这意味着任何带有 switch
语句的代码打开 enum
需要在添加枚举值后仍然保持有效(可执行)代码。如果 method
一开始是有效的,则在二进制兼容更改后它不会变得无效(因为有一个 return 路径没有 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 设计。
如果我使用 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 的每个地方,所以我问自己是否有更好的方法,以便我要么避免 enum 并使用其他概念,要么我可以确保 enum 的每个值都用于该段代码。
有 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> 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;
}
}
您可以将处理方法作为抽象方法放在 TaskType 中,然后在枚举中的每个任务中重写它。如果您创建一个界面,可能是一个更好的主意,例如:
public interface Task {
void process();
}
然后你要么让你的枚举实现这个接口。或者,可能更好的是,您创建实现此接口的具体 classes。每个任务类型 class。
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
}
如果需要,您甚至可以计算元素被计数的次数
您可以在枚举上做 switch case,如果命中默认值则失败:
switch(taskType ){
case TYPE_ONE: ... break;
case TYPE_TWO: ... break;
case TYPE_THREE: ... break;
default:
throw new IllegalStateException("Unsupported task type:"+taskType);
}
据我所知你做不到 "automatically"。
为了尽量减少忘记为新值添加 if/case 的风险,您可以为每个枚举值添加一个 "service" class 和一个为枚举提供特定服务的工厂值。
例如而不是:
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();
}
这样一来,您只有一处必须添加新枚举值的处理。
我想你是说你希望编译器告诉你所有枚举的值都被考虑了。
很遗憾,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
}
... 并依靠编译器告诉您是否遗漏了 switch 案例中的枚举值之一。
可惜不行!!以上反正是编译错误。根据 JLS 可达性规则,switch 语句需要 default:
分支才能使该方法有效。 (或者你可以在最后加一个return
...)
这种奇怪现象是有充分理由的。 JLS 二进制兼容性规则指出,向 enum
添加新值是二进制兼容更改。这意味着任何带有 switch
语句的代码打开 enum
需要在添加枚举值后仍然保持有效(可执行)代码。如果 method
一开始是有效的,则在二进制兼容更改后它不会变得无效(因为有一个 return 路径没有 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 设计。