Java 多个If-else或enum-哪个更可取,为什么?

Java 多个If-else或enum-哪个更可取,为什么?,java,enums,if-statement,code-design,Java,Enums,If Statement,Code Design,以下是原始代码: public class FruitGrower { public void growAFruit(String type) { if ("wtrmln".equals(type)) { //do watermelon growing stuff } else if ("ppl".equals(type)) { //do apple growing stuff } else

以下是原始代码:

public class FruitGrower {
    public void growAFruit(String type) {
        if ("wtrmln".equals(type)) {
            //do watermelon growing stuff
        } else if ("ppl".equals(type)) {
            //do apple growing stuff
        } else if ("pnppl".equals(type)) {
            //do pineapple growing stuff
        } else if ("rng".equals(type)) {
            //do orange growing stuff
        } else {
            // do other fruit growing stuff
        }
    }
}
我就是这样改变的:

public class FruitGrower {
    enum Fruits {
        WATERMELON {
            @Override
            void growAFruit() {
                //do watermelon growing stuff
            }
        },

        APPLE {
            @Override
            void growAFruit() {
                //do apple growing stuff
            }
        },

        PINEAPPLE {
            @Override
            void growAFruit() {
                //do pineapple growing stuff
            }
        },

        ORANGE {
            @Override
            void growAFruit() {
                //do orange growing stuff
            }
        },

        OTHER {
            @Override
            void growAFruit() {
                // do other fruit growing stuff
            }
        };
        static void grow(String type) {
            if ("wtrmln".equals(type)) {
                WATERMELON.growAFruit();
            } else if ("ppl".equals(type)) {
                APPLE.growAFruit();
            } else if ("pnppl".equals(type)) {
                PINEAPPLE.growAFruit();
            } else if ("rng".equals(type)) {
                ORANGE.growAFruit();
            } else {
                OTHER.growAFruit();
            }
        };
        abstract void growAFruit();
    }


    public void growAFruit(String type) {
        Fruits.grow(type);
    }
}
我看到
enums
代码较长,可能不像
if else
代码那样清晰,但我认为这样更好,有人能告诉我,为什么我错了(或者可能我错了)


UPD-将源代码更改为更具体的问题。我将重新表述这个问题:使用enum而不是if-else有什么问题吗?

enum是一个不错的选择,但是您可以像这样显著改进代码:

public static String grow(String type) {
    return Fruits.valueOf(type.toUpperCase()).gimmeFruit();
};
public static String grow(String type) {
    Fruits /*btw enums should be singular */ fruit = Fruits.OTHER;
    for(Fruits candidate : Fruits.values()){
        if(candidate.name().equalsIgnoreCase(type)){
            fruit = candidate;
            break;
        }
    }
    return fruit.gimmeFruit();
};
static String grow(Fruits type) {
    return type.gimmeFruit();
}
enum Fruits {
   WATERMELON("watermelon fruit"),
   APPLE("apple fruit"); //...

   private final String gimme;

   private Fruits(String gimme) {
      this.gimme = gimme;
   }

   String gimmeFruit() { return this.gimme; }       
}
static void grow(String type) {
   Fruit f = map.get(type);
   if (f == null) {
       OTHER.growFruit();
   }
   else {
       f.growFruit();
   }
}
哦,你需要一个默认案例,这会让事情变得更棘手。当然,您可以这样做:

public static String grow(String type) {
    try{
        return Fruits.valueOf(type.toUpperCase()).gimmeFruit();
    }catch(IllegalArgumentException e){
        return Fruits.OTHER.gimmeFruit();
    }
};
但那太难看了。我想我应该这样做:

public static String grow(String type) {
    return Fruits.valueOf(type.toUpperCase()).gimmeFruit();
};
public static String grow(String type) {
    Fruits /*btw enums should be singular */ fruit = Fruits.OTHER;
    for(Fruits candidate : Fruits.values()){
        if(candidate.name().equalsIgnoreCase(type)){
            fruit = candidate;
            break;
        }
    }
    return fruit.gimmeFruit();
};
static String grow(Fruits type) {
    return type.gimmeFruit();
}
enum Fruits {
   WATERMELON("watermelon fruit"),
   APPLE("apple fruit"); //...

   private final String gimme;

   private Fruits(String gimme) {
      this.gimme = gimme;
   }

   String gimmeFruit() { return this.gimme; }       
}
static void grow(String type) {
   Fruit f = map.get(type);
   if (f == null) {
       OTHER.growFruit();
   }
   else {
       f.growFruit();
   }
}
此外,如果所有枚举方法都返回值,则应重构设计,以便在构造函数中初始化值,并在枚举类中定义的方法中返回值,而不是返回单个项:

public enum Fruit{
    WATERMELON("watermelon fruit"),
    APPLE("apple fruit")
    // etc.

    ;
    private final String innerName;
    private Fruit(String innerName){ this.innerName = innerName; }
    public String getInnerName(){ return this.innerName; }
}

为了更干净,您只做了一半的更改。grow方法应更改如下:

public static String grow(String type) {
    return Fruits.valueOf(type.toUpperCase()).gimmeFruit();
};
public static String grow(String type) {
    Fruits /*btw enums should be singular */ fruit = Fruits.OTHER;
    for(Fruits candidate : Fruits.values()){
        if(candidate.name().equalsIgnoreCase(type)){
            fruit = candidate;
            break;
        }
    }
    return fruit.gimmeFruit();
};
static String grow(Fruits type) {
    return type.gimmeFruit();
}
enum Fruits {
   WATERMELON("watermelon fruit"),
   APPLE("apple fruit"); //...

   private final String gimme;

   private Fruits(String gimme) {
      this.gimme = gimme;
   }

   String gimmeFruit() { return this.gimme; }       
}
static void grow(String type) {
   Fruit f = map.get(type);
   if (f == null) {
       OTHER.growFruit();
   }
   else {
       f.growFruit();
   }
}
水果
应该改名为
水果
:苹果是水果,不是水果


如果确实需要保留字符串类型,则定义一个方法(例如,在枚举类本身中)返回与每个类型关联的结果。但是大多数代码应该使用水果而不是字符串。

我支持Sean Patrick Floyd关于枚举的观点,但我想补充一点,您可以通过使用以下方案更显著地缩短代码事件:

public static String grow(String type) {
    return Fruits.valueOf(type.toUpperCase()).gimmeFruit();
};
public static String grow(String type) {
    Fruits /*btw enums should be singular */ fruit = Fruits.OTHER;
    for(Fruits candidate : Fruits.values()){
        if(candidate.name().equalsIgnoreCase(type)){
            fruit = candidate;
            break;
        }
    }
    return fruit.gimmeFruit();
};
static String grow(Fruits type) {
    return type.gimmeFruit();
}
enum Fruits {
   WATERMELON("watermelon fruit"),
   APPLE("apple fruit"); //...

   private final String gimme;

   private Fruits(String gimme) {
      this.gimme = gimme;
   }

   String gimmeFruit() { return this.gimme; }       
}
static void grow(String type) {
   Fruit f = map.get(type);
   if (f == null) {
       OTHER.growFruit();
   }
   else {
       f.growFruit();
   }
}
此外,“成长”方法也值得怀疑。不应该是这样吗

public static String grow(Fruits f) {
   return f.gimmeFruit();
}

您还可以通过使用变量存储gimmeFruit值并使用构造函数初始化来改进它

(我还没有编译过这个,所以可能有一些语法错误)

编辑:
如果grow方法的类型不是相同的字符串,则使用映射定义要枚举的类型的匹配项,并从映射返回查找。

我想您需要一个
映射(或

此映射可以由枚举的构造函数或静态初始值设定项自动填充。(如果某些水果有别名,它甚至可以在同一个枚举常量上映射多个名称。)

您的
grow
方法如下所示:

public static String grow(String type) {
    return Fruits.valueOf(type.toUpperCase()).gimmeFruit();
};
public static String grow(String type) {
    Fruits /*btw enums should be singular */ fruit = Fruits.OTHER;
    for(Fruits candidate : Fruits.values()){
        if(candidate.name().equalsIgnoreCase(type)){
            fruit = candidate;
            break;
        }
    }
    return fruit.gimmeFruit();
};
static String grow(Fruits type) {
    return type.gimmeFruit();
}
enum Fruits {
   WATERMELON("watermelon fruit"),
   APPLE("apple fruit"); //...

   private final String gimme;

   private Fruits(String gimme) {
      this.gimme = gimme;
   }

   String gimmeFruit() { return this.gimme; }       
}
static void grow(String type) {
   Fruit f = map.get(type);
   if (f == null) {
       OTHER.growFruit();
   }
   else {
       f.growFruit();
   }
}

当然,你真的需要这里的绳子吗?您不应该总是使用枚举对象吗?

关于改进枚举的使用,您已经有了很好的答案。至于为什么它们优于字符串常量:

我认为最大的好处是编译时错误检查。如果我调用
growAFruit(“西瓜”)
,编译器将不知道有什么问题。既然我拼写正确,当您查看代码时,它就不会被视为一个错误。但是如果我是
WATERMELEN.growAFruit()
,编译器会立即告诉您我拼错了

您还可以将
grouafruit
定义为一组简单易读的方法,而不是一大块
如果
-
那么
-
否则
s。当您有几十个水果时,或者当您开始添加
harvestAFruit()
packageAFruit()
sellAFruit()
,等等时,这一点变得更加明显。如果没有枚举,您将复制整个大if-else块,如果您忘记添加一个案例,它将变成默认案例或什么都不做,使用枚举时,编译器可以告诉您该方法尚未实现

更多的编译器检查功能:如果您还有一个
growvegeture
方法和相关的字符串常量,那么没有什么可以阻止您调用
growvegeture(“菠萝”)
growFruit(“豌豆”)
。如果你有一个<代码>“西红柿”< /代码>常量,知道你是否认为它是水果或蔬菜的唯一方法是读取相关方法的源代码。同样,使用枚举,编译器可以立即告诉您是否做错了什么

另一个好处是它将相关常数分组在一起,并为它们提供一个合适的家。另一种选择是在某个类中抛出一堆
公共静态final
字段,这些字段碰巧使用它们,或者卡住了一个常量接口。充满常量的接口甚至没有意义,因为如果这就是您所需要的,那么定义枚举要比写出接口容易得多。此外,在类或接口中,可能会意外地对多个常量使用相同的值

它们也非常适合。要获取枚举的所有值,您只需调用
Fruit.values()
,而对于常量,您必须创建并填充自己的数组。或者,如果只是像您的示例中那样使用文本,则没有有效值的授权列表

奖励回合:IDE支持

  • 使用枚举,您可以使用IDE的自动完成功能和自动重构
  • 您可以在Eclipse中使用“查找引用”之类的方法来查找枚举值,而您必须执行纯文本搜索来查找字符串文字,这通常也会返回大量误报(如果您使用静态最终常量,则可能有人在某处使用了字符串文字)

不使用枚举的主要原因是,如果您在编译时不知道所有可能的值(即,您需要在程序运行时添加更多值)。在这种情况下,您可能希望将它们定义为类继承权。另外,不要把一堆不相关的常量扔进一个枚举,然后就到此为止。应该有某种公共线程连接这些值。如果更合适的话,您总是可以创建多个枚举。

我经常在枚举中实现一个方法来解析给定字符串并返回相应的枚举常量。我总是将此方法命名为
parse(String)

有时我重载此方法以解析enum constan