【问题标题】:Are enums less maintainable than public static final constants?枚举是否比公共静态最终常量更难维护?
【发布时间】:2013-12-31 18:39:30
【问题描述】:

我最近在和朋友讨论枚举与公共静态最终常量。我告诉他公共静态最终常量比枚举更易于维护,有时更快(android 开发者文档证实了这一点),也更方便。我还说过,在使用枚举时也会失去功能:

  1. 您不能扩展枚举。
  2. 您不能实例化枚举。

然后他说,如果您需要实例化或扩展枚举,则不应使用枚举。然后我回答说这就是为什么我们应该只使用常量,因为它更易于维护;如果中间项目我们需要实例化一个枚举或扩展它怎么办?然后我们将不得不改变一切。

为了说明我的观点,我制作了一个演示枚举与常量的示例:

public enum WeekDay {
/*
 * We will start at 1 for demonstration
 */
SUNDAY("Sunday", 1), MONDAY("Monday", 2), TUESDAY("Tuesday", 3), WEDNESDAY(
        "Wednesday", 4), THURSDAY("Thursday", 5), FRIDAY("Friday", 6), SATURDAY(
        "Saturday", 7);
/*
 * Notice we cannot do this...This is where enums fail.
 */
// LUNES("lunes",1), MARTES("martes",2);

private String dayName;
private int dayIndex;

private WeekDay(String dayName, int dayIndex) {
    this.dayName = dayName;
    this.dayIndex = dayIndex;
}

public String getDayName() {
    return dayName;
}

public void setDayName(String dayName) {
    this.dayName = dayName;
}

public int getDayIndex() {
    return dayIndex;
}

public void setDayIndex(int dayIndex) {
    this.dayIndex = dayIndex;
}

@Override
public String toString() {
    return this.dayName + ":  " + this.dayIndex;
}

}

如果我们也需要西班牙工作日怎么办?枚举不足,因为您无法扩展它(您必须执行一些复制和粘贴操作)。

将枚举与此进行对比:

public class WeekDayClass {

    private int dayIndex;
    private String dayName;

    public WeekDayClass(int dayIndex, String dayName) {
        super();
        this.dayIndex = dayIndex;
        this.dayName = dayName;
    }

    public int getDayIndex() {
        return dayIndex;
    }

    public void setDayIndex(int dayIndex) {
        this.dayIndex = dayIndex;
    }

    public String getDayName() {
        return dayName;
    }

    public void setDayName(String dayName) {
        this.dayName = dayName;
    }

    @Override
    public String toString() {
        return this.dayName + ":  " + this.dayIndex;
    }

    abstract static class Constants {

    }


    public static void main(String[] args) {
        WeekDayClass init = new WeekDayClass(10, "I can init new days here");

    }
}

然后我可以扩展它并制作 AmericanWeekDays:

public class AmericanWeekDay extends WeekDayClass {
    public AmericanWeekDay(int dayIndex, String dayName) {
        super(dayIndex, dayName);
    }

    static class AmericanConstants extends Constants {
        public static final WeekDayClass SUNDAY = new WeekDayClass(1, "Sunday");
        public static final WeekDayClass MONDAY = new WeekDayClass(2, "Monday");
        /*
         * And so on...
         */
    }

}

或西班牙工作日:

 public class SpanishWeekDays extends WeekDayClass {

    public SpanishWeekDays(int dayIndex, String dayName) {
        super(dayIndex, dayName);
    }

    static class SpanishConstants extends Constants {
        public static final SpanishWeekDays LUNES = new SpanishWeekDays(2, "lunes");
        /*
         * And so on...
         */
    }

}

还要走得更远:

public class WeekDayClass {

    private int dayIndex;
    private String dayName;

    public WeekDayClass(int dayIndex, String dayName) {
        super();
        this.dayIndex = dayIndex;
        this.dayName = dayName;
    }

    public int getDayIndex() {
        return dayIndex;
    }

    public void setDayIndex(int dayIndex) {
        this.dayIndex = dayIndex;
    }

    public String getDayName() {
        return dayName;
    }

    public void setDayName(String dayName) {
        this.dayName = dayName;
    }

    @Override
    public String toString() {
        return this.dayName + ":  " + this.dayIndex;
    }

    static class AmericanConstants {
        /*
         * Insert Constants Here
         */

    }

    static class SpanishConstants {
        /*
         * Insert Constants Here
         */
    }

}

我了解使用枚举您也许可以使用数据结构(列表)来解决这个问题,这样您就可以适应这个缺点,但为什么要麻烦呢?通过使用公共静态常量,我获得了从基类的继承、更简洁的代码、可能更短的代码以及更易于维护。

我还读到您可以使用枚举来更好地设计“输入参数”,但您也可以对公共静态最终常量执行相同的操作,如上所示。

枚举的优点是可以在 switch 语句中使用,并且具有继承的枚举方法,如 values()。如果需要,这些方法也可以在“公共静态最终常量”类中复制。除了开关之外,我没有看到任何枚举优势。

总之,枚举真的比公共静态最终常量更好吗?如果是这样,我哪里出错了?他们是我缺少的东西吗?

编辑: 您也不能在枚举中使用泛型。

【问题讨论】:

  • static final 变量如何扩展?
  • 我的意思是基类
  • 对于任何想知道的人,android R 系统使用了我在上面发布的类似结构。
  • “枚举比公共静态最终常量更难维护吗?” .... 在得出适合您的答案时,必须考虑很多方面用例。任何试图为这个问题提供一个决定性的、普遍的“是-否”答案的尝试都将失败。一个特别重要的方面是可串行化。编写一个健壮、正确、实例控制和安全可串行化的Java类不是小事。使用 Java 枚举,您可以免费获得它。

标签: java performance enums constants maintainability


【解决方案1】:

枚举给你带来的好处比你想象的要多得多,虽然有时需要常量,但这种情况可能是枚举的胜利。

首先,“English weekday”和“Spanish weekday”之间没有真正的区别,它们代表相同的值。因此,最好的解决方案是通过某种本地化方法独立于实际值进行转换为字符串。值不会随着语言而改变,它们的表示会改变。

这完全可以通过枚举快速轻松地实现。就这样写(有点伪代码):

public enum Weekday {
    MONDAY,
    TUESDAY,
    WEDNESDAY,
    ...;

    public String toLocalizedString(Language l) {
        // interact with localization files
    }
}

您将外部表示和内部表示的概念混为一谈。您的数据应尽可能同质,因为只有一个星期一。它可能被称为不同的东西,但它仍然是相同的值。

不过,枚举还可以免费为您带来很多好处,从长远来看,这会使您的代码更加清晰和可维护。类型检查、== 比较和switch 中的可用性是少数,没有样板可言。

【讨论】:

  • “WeekDay”枚举只是一个(坏的)例子。对于这个例子,使用 Language 作为参数是“正确的方式”。至于开关,我们不能只做一个 Map 吗?
  • 在这个例子中更好的表达方式是西班牙或美国的工作日在其构造函数中是否有一个额外的参数。例如,我们可以有一个类“ChristianWeekDay”,超类是“WeekDays”,然后在构造函数中有一个额外的参数作为“boolean isSabbath”。在这种情况下调整枚举会很困难。
  • @horvste 在这一点上,您正在深入了解极其复杂的本地化差异。从历史上看,本地化日期和时间是最难做到的事情之一。不要自己滚。至于一般问题,在我看来,复杂的问题不应该使用类常量枚举,你应该使用真正的类层次结构并实现日历类等。我没有曾经在 Java 中看到过类常量枚举比内置枚举功能更好的情况。
  • 但是在基于枚举的常量之后使用类层次结构是行不通的,除非你想重新开始。似乎使用问题中所示的层次结构可以让您以方便为代价获得更大的灵活性。为什么要为了方便而牺牲灵活性?
  • @horvste 我看不出从枚举切换到完整的类层次结构比使用类常量更难。无论哪种方式,您都错误地设计了系统,您需要进行重大重构。枚举和类常量都应该用于一组有限的不会改变的常量值;您不能“扩展”其中任何一个,如果您需要这种类型的功能,请使用类。何时使用枚举以及何时使用类通常很清楚,但如果您选择类常量而不是枚举,那么灵活性仍然没有真正的优势。
【解决方案2】:

我认为您对枚举的使用已经走得太远,然后得出结论认为它们没有用。

枚举只是告诉您有 limitedpredefined 数量的选项可供选择。 仅此而已。例如,当您看到一个参数是枚举(假设是 State)并且它有 3 个值(Pending、InProgress、Closed)时,您就知道某个对象的状态可以具有其中一个值,并且只能具有其中一个值。

枚举提供了一种简单的方法来验证是否使用了正确的值,因为您在编码时无法轻易选择不正确的值。它们也是一种记录方式,因为您可以轻松查看可用的选项。

【讨论】:

  • 规划未来不是更好(更易于维护)吗?你用枚举来限制你的选择。例如,我有枚举“状态”。在假设下,“状态”枚举具有特定于对象“状态”的方法,而不仅仅是“标记”,最好保留“多态”属性,以便我们可以重用代码,如果我们需要,也许用不同的对象?
  • 如果你需要一个更复杂的结构,你可以创建一个类、方法等。枚举应该是一个简单的结构——你并不总是需要过于复杂。限制选项正是枚举的用途。您可以随时为它们添加更多价值。
【解决方案3】:

如果没有用,枚举就不会存在——常量也是如此。就像螺丝刀可以卸下螺丝而锤子可以卸下钉子一样 - 程序员“工具箱”中的不同工具可用于独特而重要的目的。我建议阅读更多有关枚举和常量的内容,我想您会发现它们存在的原因以及何时使用它们。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-04-15
    • 1970-01-01
    • 2011-02-06
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多