【问题标题】:Java Enums for Hierarchical/Tree like Structure用于分层/树状结构的 Java 枚举
【发布时间】:2014-06-01 03:23:08
【问题描述】:

我需要创建一个需要表示以下内容的结构(对于类别和子类别)。它只有一层深。我正在考虑使用 Java Enums 来做这件事,但不确定如何表示这种层次结构。

代表设备的 java 对象(业务对象)将同时具有类别和子类别属性,我想使用枚举而不是使用整数代码,例如 100、1 等。有些设备只有类别,但没有子类别(如以下示例中的 300)。

100  Switch  
     1   Interior
     2   Exterior
200  Security Sensor     
     1   Door Sensor
     2   Leak Sensor
     3   Motion Sensor
300  Camera

感谢任何帮助。

谢谢

【问题讨论】:

  • 看看 Java 的 EnumMap 或者这个SO question
  • 您找到“正确”答案了吗?请勾选您认为最正确的一项。

标签: java enums


【解决方案1】:

这篇java.dzone 文章展示了一个漂亮的分层枚举示例:

public enum OsType {
    OS(null),
        Windows(OS),
            WindowsNT(Windows),
                WindowsNTWorkstation(WindowsNT),
                WindowsNTServer(WindowsNT),
            Windows2000(Windows),
                Windows2000Server(Windows2000),
                Windows2000Workstation(Windows2000),
            WindowsXp(Windows),
            WindowsVista(Windows),
            Windows7(Windows),
            Windows95(Windows),
            Windows98(Windows),
        Unix(OS) {
                @Override
                public boolean supportsXWindows() {
                    return true;
                }
            },
            Linux(Unix),
            AIX(Unix),
            HpUx(Unix),
            SunOs(Unix),
    ;
    private OsType parent = null;

    private OsType(OsType parent) {
        this.parent = parent;
    }
}

本文展示了许多您可以通过此设置完成的小技巧。

【讨论】:

    【解决方案2】:

    我认为this answer 对您来说是一个很好的解决方案。使用这样的类型层次结构:

    public enum Component {
        Interior(Part.Switch),
        Exterior(Part.Switch),
        DoorSensor(Part.SecuritySensor),
        LeakSensor(Part.SecuritySensor),
        MotionSensor(Part.SecuritySensor),
        Camera(Part.Camera);
    
        private final Part kindOf;
    
        Component(Part kindOf) {
            this.kindOf = kindOf;
        }
    
        enum Part {
            Switch, SecuritySensor, Camera
        }
    
    }
    

    更多细节可以在Effective Java 2nd Edition的章节枚举中找到。

    【讨论】:

      【解决方案3】:

      您想用枚举而不是继承来表达这一点似乎很不寻常。枚举的问题在于它们本质上是编译时常量,如果您想在层次结构中生成更多信息,则必须添加更多枚举。

      这可能会变得混乱,很快。它也与枚举的真正目的略有矛盾——作为预定义的常量而不是分层实体。

      我建议使用一个名为CategoryBase 的抽象类来绘制所有内容。然后,以此为基础创建您的继承树。

      这是一个图表:

      大部分工作实际上是保存属性,我们永远不会期望这些属性在创建后会被更改,因此我们可以让我们的抽象类来保存它们。我们也将它们设置为final,因此它们也无法修改。

      public abstract class CategoryBase {
      
          protected final int ranking;
          protected final String name;
          protected final SubCategory[] subCategories;
      
          protected CategoryBase(int ranking, String name, SubCategory... subCategories) {
              this.ranking = ranking;
              this.name = name;
              this.subCategories = subCategories;
          }
      
          public int getRanking() {
              return ranking;
          }
      
          public String getName() {
              return name;
          }
      
          public SubCategory[] getSubCategories() {
              return subCategories;
          }
      }
      

      从那里开始,我们可以以此为基础创建我们的标记类 - 包括 SubCategory,因为它实际上只是一个以不同方式表示的信息的持有者。

      这也将使编写标记类变得简单明了。例如这里是Camera

      public class Camera extends CategoryBase {
      
          protected Camera(int ranking, String name) {
              super(ranking, name);
          }
      }
      

      它与SubCategory 有着惊人的相似之处——SubCategory 没有任何嵌套的SubCategorys,因此我们不会向构造函数的可变参数部分传递任何内容。

      对于确实SubCategorys的东西,我们需要在构造时实例化它们。这里以SecuritySensor 为例。

      public class SecuritySensor extends CategoryBase {
      
          public SecuritySensor(int ranking, String name) {
              super(ranking, name,
                      new SubCategory(1, "Door Sensor"),
                      new SubCategory(2, "Leak Sensor"),
                      new SubCategory(3, "Motion Sensor"));
          }
      }
      

      这种方法也为您在排名方面提供了一些灵活性 - 如果您希望能够在运行时指定子类别的确切排名,您可以将此构造函数替换为支持可变参数的构造函数签名。

      【讨论】:

        【解决方案4】:

        您可以执行以下代码。但这可能不是您想要的。您可以将子枚举添加到父枚举的构造函数中,例如here

        enum Stuff {
            Swich,Sensor,Camera;
            enum swich {
                interior,exterior;
                enum Where {
                    bathroom,kitchen
                }
            }
            enum sensor {
                door,leak,motion
            }
        }
        

        【讨论】:

          【解决方案5】:

          也许重新考虑并在这里使用整数。但是,它们不是基于十进制系统(第一级为 100、200、300、...;第二级为 1、2、3、...),而是基于二进制表示。

          // top-level
          public static final int SWITCH          = 1 << 16;
          public static final int SECURITY_SENSOR = 2 << 16;
          public static final int CAMERA          = 4 << 16;
          
          // sub-types of 'switch'
          public static final int INTERIOR = SWITCH | 1;
          public static final int EXTERIOR = SWITCH | 2;
          
          // sub-types of 'security sensor'
          public static final int DOOR_SENSOR   = SECURITY_SENSOR | 1;
          public static final int LEAK_SENSOR   = SECURITY_SENSOR | 2;
          public static final int MOTION_SENSOR = SECURITY_SENSOR | 4;
          

          这允许您进行弱形式的继承测试:

          if (value == SWITCH) {
             // value is a switch, but not interior or exterior
          } else if (value & SWITCH != 0) {
             // value is interior or exterior
          }
          

          【讨论】:

          • 虽然这样可以完成这项工作,但我强烈反对在几乎所有情况中使用按位算术。引用 Effective Java,第 32 条:“但是位域具有 int enum 常量的所有缺点等等。当它打印为数字时,解释位域比简单的 int enum 常量更难。此外,还有没有简单的方法来遍历位字段表示的所有元素。”
          • 谨慎地做出设计决策当然是个好主意。但仅仅因为某些东西写在书中并不意味着你必须盲目地遵循它。这实际上取决于您的用例 - 如果您需要某个功能,例如快速继承测试,而枚举本身无法提供这些功能,那么您是否要解决它取决于您,比如使用多个还是嵌套枚举,或使用不同的方法。
          • 我不建议这样做,因为它在书中,我引用了这本书,因为布洛赫的陈述非常准确。在我看来,按位运算类似于使用指针进行编程:有很多开发人员没有掌握这些概念,或者至少对这些主题有很大的问题。 Joel Spolsky 几年前写了一个nice article。因此,如果我要在一个团队中编写代码,我会省去按位运算。 SCJP 考试放弃了按位运算,因此传达了内涵:“不要使用它!”
          • 我很欣赏你的意见,但我不同意。
          【解决方案6】:

          以下代码是否满足您的要求?

          public enum Category {Switch, ...}
          public enum SubCategory {Interior, ...}
          

          【讨论】:

          • 没有。我想保持类别和子类别之间的关系。拥有 2 个单独的枚举将使您设置类别 = 100 和子类别 = 3(开关 - 运动传感器),这是错误的。谢谢。
          • @user2626222 这根本不应该发生,因为Motion Sensor 不会存在于Switch 的值中...
          • 然后您可以添加另一个枚举,例如 {enum Category cate;枚举子类别子类别; SwitchInterior(Switch, Interior), SwitchExterior(Switch, Exterior)...} 然后在你的对象类中使用这个枚举
          猜你喜欢
          • 2016-11-18
          • 2016-05-25
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2019-02-07
          相关资源
          最近更新 更多