【问题标题】:Create an extended "enum" with runtime attributes创建具有运行时属性的扩展“枚举”
【发布时间】:2011-06-17 06:37:35
【问题描述】:

我想简化特定预定义对象的选择。

我目前有一个枚举定义为

ArtworkType { Poster, Banner, Other }

我想为这些 ArtworkTypes 添加属性,以便我可以在其他地方的代码中使用它们。 ArtworkTypes 的属性要么是预定义的静态标签,要么是从填充到 Properties() 类中的外部配置文件中填充的。

理想情况下,我想做一些简单的事情

ArtworkType.Poster.getWidth();

如果我必须使用最终类,我认为必须使用类似的东西会更复杂

ArtworkType.getWidth(TypeEnum.Poster);


编辑:感谢以下答案,我得出结论,虽然我可以使用枚举来完成,但最好使用外部类(例如 ArtworkUtil)来检索属性我在追

这是迄今为止我创建的示例枚举代码(省略了错误检查):

public enum ArtworkType {
    Poster("poster"), Banner("banner"), Other("other");

    private String type;
    private Dimension dimension;

    private ArtworkType(String type) {
        this.type = type;
        this.dimension = new Dimension(Properties.getProperty("width."+type), Properties.getProperty("height."+type);
    }

    public Dimension getDimension() {
        return dimension;
    }
}

虽然我知道这违反了严格枚举的原则,但与枚举相关的值是静态的(在应用程序期间),它可能是两个弊端中的较小者。

我能想到的唯一其他方法是创建一个“ArtworkUtil”类,该类创建一个集合并将所有必需的属性填充到一个对象中并将其存储在集合中。

在代码中访问该类会使其更难读(除非我遗漏了什么?)

【问题讨论】:

    标签: java class enums


    【解决方案1】:

    枚举是编译时常量。您不能从属性文件初始化它们。

    但是,它们可以具有构造函数、方法和字段。 它们还可以在主体中具有基本方法实现,这些实现在各个枚举条目中被覆盖。

    public enum Shape{
        SQUARE(10),
        RECTANGLE(10, 15),
        CIRCLE(10){
            @Override
            public double getArea(){
                return Math.PI * Math.pow(((double) getWidth()) / 2, 2);
            }
        },
        OVAL(10, 15){
            @Override
            public double getArea(){
                return Math.PI * (getWidth()) / 2 * (getHeight()) / 2;
            }
        };
    
        private Shape(final int dim){ this(dim, dim); }
        private Shape(final int width, final int height){
            this.width = width; this.height = height;
        }
    
        private final int width;
        private final int height;
    
        public double getArea(){ return width * height; }
    
        public final int getWidth(){ return width; }
    
        public final int getHeight(){ return height; }
    
    }
    

    测试代码:

    public static void main(final String[] args){
        for(final Shape shape : Shape.values()){
            System.out.printf("Shape: %s, area: %1.2f\n", shape,
                shape.getArea());
        }
    }
    

    输出:

    形状:SQUARE,面积:100.00
    形状:矩形,面积:150.00
    形状:圆形,面积:78.54
    形状:椭圆形,面积:117.81


    处理编译时限制

    枚举是编译时常量,所以你不能使用非常量值来初始化它们,这意味着你不能这样做:

    SQUARE(SomeClass.getSquareValue())
    

    所以你基本上有三个选择:

    1. 从你的属性文件自动生成枚举

      使用像 Maven 这样的构建工具,让一些代码为你生成一个枚举文件,并转换这种格式:

      ENUMNAME=property.value
      

      进入这个枚举条目:

      ENUMNAME("property.value")
      

      将生成的枚举 .java 添加到您的编译源中。从 Java 的角度来看,这是最干净的方法,因为您具有绝对的编译时安全性。问题是:每次修改属性文件都需要重新编译。

    2. 延迟初始化枚举 (argh)

      使用属性键初始化枚举项,当第一次调用枚举的方法时,从类路径中查找属性,将其缓存以供进一步使用。这是一个糟糕的 hack,但有时它很有用。

    3. 将所有值从外部传递到枚举中,使用它们作为策略:

      示例:此枚举使用每个枚举条目的不同前缀查找系统属性。

      public enum Lookup{
           ADDRESS("$1".address),
           NAME("$1".name);
           private final String pattern;
           private Lookup lookup(String pattern){
               this.pattern=pattern;
           }
           public final String lookupProperty(String input){
               return System.getProperties().get(
                   this.pattern.replace("$1",input)
               );
           }
       }
      

    【讨论】:

    • 也许我并没有像我的问题那样清楚。我想采用当前的 Enum 并以符合这些要求的方式对其进行更改。我不在乎它是否保持枚举或者我是否必须将它重新编码为一个类
    • 好的,我想我现在明白了。我可以将 SQUARE(10) 更改为类似 SQAURE(ExternalClass.getValue("square") 吗?
    • @Omerton 不,你不能这不是编译时常量。
    • 你不能,但幸运的是你不必这样做。
    【解决方案2】:

    如果你想要一个 getWidth() 方法,你可以像任何其他类类型一样将它添加到枚举中。也许你可以澄清你的担忧。

    来自enum上的文档

    public enum Planet {
        MERCURY (3.303e+23, 2.4397e6),
        VENUS   (4.869e+24, 6.0518e6),
        EARTH   (5.976e+24, 6.37814e6),
        MARS    (6.421e+23, 3.3972e6),
        JUPITER (1.9e+27,   7.1492e7),
        SATURN  (5.688e+26, 6.0268e7),
        URANUS  (8.686e+25, 2.5559e7),
        NEPTUNE (1.024e+26, 2.4746e7),
        PLUTO   (1.27e+22,  1.137e6);
    
        private final double mass;   // in kilograms
        private final double radius; // in meters
        Planet(double mass, double radius) {
            this.mass = mass;
            this.radius = radius;
        }
        public double mass()   { return mass; }
        public double radius() { return radius; }
    
        // universal gravitational constant  (m3 kg-1 s-2)
        public static final double G = 6.67300E-11;
    
        public double surfaceGravity() {
            return G * mass / (radius * radius);
        }
        public double surfaceWeight(double otherMass) {
            return otherMass * surfaceGravity();
        }
    }
    

    【讨论】:

      【解决方案3】:
      public enum ArtworkType {
          Poster, Banner, Other;
      
          private static ResourceBundle properties;
      
          private static ResourceBundle getProperties() {
              if( properties == null ) {
                  properties = ResourceBundle.getBundle( "artworks");
              }
              return properties;
          }
      
          public int getWidth() {
              return Integer.parseInt( getProperties().getString( "width."+this.name() ) );
          }
        }
      

      这是您需要做什么的粗略想法。当然这不是真正的生产质量代码,为了可读性,省略了一些安全措施。

      您的属性文件将是这样的:

      artworks.properties

      width.Poster=500
      width.Banner=1900
      width.Other=-1
      

      【讨论】:

      • 我在想,如果我一直调用 getWidth() 并且每次都必须读取属性 ResourceBundle 然后转换为 Int,那么可能会影响性能?我认为没有办法将它与枚举一起存储并初始化一次(有点像最终变量?)
      • @Omertron 当然可以,但是您可以在首次调用该方法时将值存储在字段中。我的例子是一个非常简单的例子,只是为了展示这个概念是如何工作的。尽管即使在我的示例中,您也只加载了一次 ResourceBundle,所以我认为开销并没有那么严重。
      • @biziclop 这种方法的问题在于它违反了枚举所代表的一切。我知道这是合法的,有时是必要的,但我仍然认为这是一种不好的做法。
      • 所以如果我理解正确的话,我可以有 private int width = getProperties().getString("width."+this.name(); private int height = getProperties().getString("height ."+this.name(); 然后用 public int getWidth() { return width; } 返回
      • @Sean Patrick Floyd 我同意,这有点违反枚举的用途,但如果你把所有这些逻辑放在一个外部类中,比如ArtworkUtil,那么你就违反了 OO 原则。因此,如果您需要使用外部数据来支持您的枚举(如果它们出现在 UI 中,您必须这样做),您就无法摆脱这种困境。就个人而言,虽然我可能宁愿违反 OO 原则而不是枚举。
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2014-11-18
      • 2012-07-09
      相关资源
      最近更新 更多