除了看起来不太好的 if else 之外,还有其他方法可以在 Java 中实现 switch case。一组值将组合在一起,根据选择相应的方法必须执行。
14 回答
如果你的代码周围有很多 switch/case 语句,它们会让你发疯。
您可以选择重构:用多态性替换条件。
假设您有一个用于将信息保存到不同设备的软件:定义了 4 个持久性操作:获取、保存、删除、更新,可以通过 N 种持久性机制(平面文件、网络、RDBMS、 XML 等)。
你的代码必须支持它们,所以在 4 个不同的地方你有这个:
前
class YourProblematicClass {
....
public void fetchData( Object criteria ) {
switch ( this.persitanceType ) {
case FilePersistance:
// open file
// read it
// find the criteria
// build the data
// close it.
break;
case NetWorkPersistance:
// Connect to the server
// Authenticate
// retrieve the data
// build the data
// close connection
break;
case DataBasePersistace:
// Get a jdbc connection
// create the query
// execute the query
// fetch and build data
// close connection
break;
}
return data;
}
保存/删除/更新相同
public void saveData( Object data) {
switch ( this.persitanceType ) {
case FilePersistance:
// open file, go to EOF, write etc.
break;
case NetWorkPersistance:
// Connect to the server
// Authenticate
// etc
break;
case DataBasePersistace:
// Get a jdbc connection, query, execute...
break;
}
}
等等....
public void deleteData( Object data) {
switch ( this.persitanceType ) {
case FilePersistance:
break;
case NetWorkPersistance:
break;
case DataBasePersistace:
break;
}
}
public void updateData( Object data) {
switch ( this.persitanceType ) {
case FilePersistance:
break;
case NetWorkPersistance:
break;
case DataBasePersistace:
break;
}
}
使用 switch/case 语句会出现问题:
每次要添加新类型时,都必须在每个部分中插入新的开关/案例。
很多时候,某些类型是相似的,它们不需要不同的开关/外壳(你可以级联它们)
它们是其他一些,有时它们略有不同
您甚至可能需要在运行时加载不同的类型(如插件)
所以这里的重构将是添加一个接口或抽象类型,并让不同的类型实现该接口并将责任委托给该对象。
所以你会有这样的事情:
后
public interface PersistenceManager {
public void fetchData( Object criteria );
public void saveData( Object toSave );
public void deleteData( Object toDelete );
public void updateData( Object toUpdate );
}
以及不同的实现
public class FilePersistence implements PersistanceManager {
public void fetchData( Object criteria ) {
// open file
// read it
// find the criteria
// build the data
// close it.
}
public void saveData( Object toSave ) {
// open file, go to EOF etc.
}
public void deleteData( Object toDelete ){
....
}
public void updateData( Object toUpdate ){
....
}
}
其他类型将根据其逻辑实现。网络将处理套接字和流,DB 将处理 JDBC、ResultSets 等。XML 与节点等。
public class NetworkPersistence implements PersistanceManager {
public void fetchData( Object criteria ) {
// Socket stuff
}
public void saveData( Object toSave ) {
// Socket stuff
}
public void deleteData( Object toDelete ){
// Socket stuff
}
public void updateData( Object toUpdate ){
// Socket stuff
}
}
public class DataBasePersistence implements PersistanceManager {
public void fetchData( Object criteria ) {
// JDBC stuff
}
public void saveData( Object toSave ) {
// JDBC stuff
}
public void deleteData( Object toDelete ){
// JDBC stuff
}
public void updateData( Object toUpdate ){
// JDBC stuff
}
}
最后你只需要委托调用。
之后:
public YouProblematicClass { // not longer that problematic
PersistamceManager persistance = // initialize with the right one.
public void fetchData( Object criteria ) {
// remove the switch and replace it with:
this.persistance.fetchData( criteria );
}
public void saveData( Object toSave ) {
// switch removed
this.persistance.saveData( toSave );
}
public void deleteData( Object toDelete ){
this.persistance.deleteData( toDelete );
}
public void updateData( Object toUpdate ){
this.persistance.updateData( toUpdate );
}
}
因此,您只需根据类型为持久性管理器创建一次正确的实例。然后所有的调用都由多态性解决。这是面向对象技术的关键特性之一。
如果您决定需要另一个持久性管理器,您只需创建新的实现并分配给该类。
public WavePersistance implements PersistanceManager {
public void fetchData( Object criteria ) {
// ....
}
public void saveData( Object toSave ) {
// ....
}
public void deleteData( Object toDelete ){
// ....
}
public void updateData( Object toUpdate ){
// ....
}
}
大概您正在努力满足案例保持不变的要求。通常这是一种代码气味,但您可以做一些事情。您可能想提出并链接到另一个详细说明您尝试转换的问题的问题。
Map<String,Object> map = new HasMap<String,Object>();
// ... insert stuff into map
// eg: map.add("something", new MyObject());
String key = "something";
if (map.contains(key)) {
Object o = map.get(key);
}
在上面的示例中,您可能希望映射到“处理程序”,例如
interface Handler {
public void doSomething();
}
然后这一切都变成了查找。
if (map.contains(key)) { map.get(key).doSomething(); }
再次,它有点气味,所以请发布一个说明推理的问题。
重构代码以使用多态性可以消除对 switch 语句的需要。但是,switch 有一些合法用途,因此取决于您的情况。
丑陋的系列if,else if,else
?
或者可以想象一种动态开关盒:
public interface Task<T>
{
public void doSomething(T context);
}
public Class SwitchCase<T>
{
Map<Integer,Task<T>> tasks;
Task<T> defaultTask;
public void choose(int choice, T context)
{
Task<T> t= this.tasks.get(choice);
if(t!=null) { t.doSomething(context); return;}
if(defaultTask!=null) { defaultTask.doSomething(context);}
}
}
我猜“清洁代码”根据 switch/case 与 if/else 有一个很好的章节。
此外:我认为决定是否可以通过使用 switch case、多态甚至是一个好的 if/else 来减少“噪音”并使代码更清晰是有意义的。我猜,案件的数量在这里起着重要作用。
我发布了一个典型案例,我如何用枚举替换开关盒。
在重构之前我有 enum PatternTypes
:
public enum PatternTypes {
ALPHA_CHAR, ALPHANUMERIC_CHAR, ADDITIONAL_CHAR, UNICODE_BMP_CHARS
}
和功能:
private static final String ALPHA_CHAR = "[a-zA-Z]+";
private static final String ALPHANUMERIC_CHAR = "[a-zA-Z0-9\\_]+";
private static final String ADDITIONAL_CHAR = "[a-zA-Z0-9\\_\\-\\,\\.\\s\\!\\#\\$\\&\\(\\)\\*\\+\\;\\:\\=\\?\\@\\|\\[\\]\\{\\}\\~]+";
private static final String UNICODE_BMP_CHARS = "[a-zA-Z0-9\\_\\-\\,\\.\\s\\!\\#\\$\\&\\(\\)\\*\\+\\;\\:\\=\\?\\@\\|\\[\\]\\{\\}\\~\u00A0-\uD7FF\uF900-\uFFFD]+";
/*
* Match given classAbbr with given RegEx pattern
*/
private void checkInvalidClassAbbr(String classAbbr,
PatternTypes classAbbrPattern) {
switch (classAbbrPattern) {
case ALPHA_CHAR:
checkUnmatched(classAbbr, ALPHA_CHAR, CLASS_ABBR_VAR_NAME);
break;
case ALPHANUMERIC_CHAR:
checkUnmatched(classAbbr, ALPHANUMERIC_CHAR, CLASS_ABBR_VAR_NAME);
break;
case ADDITIONAL_CHAR:
throw new MalFormedDNException("Not support Pattern Type:"
+ classAbbrPattern);
case UNICODE_BMP_CHARS:
throw new MalFormedDNException("Not support Pattern Type:"
+ classAbbrPattern);
}
}
重构PatternTypes
后修改为:
public enum PatternTypes {
/**
* RegEx patterns divided by restriction level
*/
ALPHA_CHAR("[a-zA-Z]+"),
ALPHANUMERIC_CHAR("[a-zA-Z0-9\\_]+"),
ADDITIONAL_CHAR("[a-zA-Z0-9\\_\\-\\,\\.\\s\\!\\#\\$\\&\\(\\)\\*\\+\\;\\:\\=\\?\\@\\|\\[\\]\\{\\}\\~]+"),
UNICODE_BMP_CHARS("[a-zA-Z0-9\\_\\-\\,\\.\\s\\!\\#\\$\\&\\(\\)\\*\\+\\;\\:\\=\\?\\@\\|\\[\\]\\{\\}\\~\u00A0-\uD7FF\uF900-\uFFFD]+");
public String getPatternContent() {
return patternContent;
}
private String patternContent;
PatternTypes(String patternContent) {
this.patternContent = patternContent;
}
}
和功能简化为:
/*
* Match given classAbbr with given RegEx pattern
*/
private void checkInvalidClassAbbr(String classAbbr, PatternTypes classAbbrPattern) {
if (PatternTypes.ADDITIONAL_CHAR.equals(classAbbrPattern) || PatternTypes.UNICODE_BMP_CHARS.equals(classAbbrPattern)){
throw new MalFormedDNException("RegEx pattern:" + classAbbrPattern.name() + "is not allowed for Class Abbr");
}
checkUnmatched(classAbbr, classAbbrPattern.getPatternContent(), CLASS_ABBR_VAR_NAME);
}
Hashmap 被认为对内存不友好,因此您可以为此目的使用 Enum。
例子:
class EnumExample4{
enum Season{
WINTER(5), SPRING(10), SUMMER(15), FALL(20);
private int value;
private Season(int value){
this.value=value;
}
}
public static void main(String args[]){
System.out.println(Season.WINTER.value); //This gives you 5
}}
这将使您免于编写 Switch Case 或 if 语句。
对于 switch 语句的替代,我认为最好的解决方案是使用enum。例如:考虑以下情况:-
public enum EnumExample {
OPTION1{
public double execute() {
Log.info(CLASS_NAME, "execute", "The is the first option.");
return void;
}
},
OPTION2{
public double execute() {
Log.info(CLASS_NAME, "execute", "The is the second option.");
return void;
}
},
OPTION3{
public double execute() {
Log.info(CLASS_NAME, "execute", "The is the third option.");
return void;
};
public static final String CLASS_NAME = Indicator.class.getName();
public abstract void execute();
}
上述枚举可以按以下方式使用:
EnumExample.OPTION1.execute();
希望这对你们有帮助。
你想让我做什么?为什么 Switch-Case 不够好?
快速的答案是:使用 if-else
if () {}
else if () {}
...
else if () {}
?
但我不会说它更好...
if
(连同else if
and else
)语句怎么样?虽然switch
只允许您使用对整数或枚举类型的相等性进行切换,但if
允许您使用任何布尔逻辑。
您总是可以用 替换开关if-else if-else if-else if...
,但我不明白您为什么要这样做。根据上下文switch
s 有时也可以替换为数组或哈希图。
如果字符串是静态的,您可以创建一个 ENUM。并打开它。