使用运行时属性创建扩展的“枚举”

时间:2011-01-28 14:04:52

标签: java class enums

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

我目前将枚举定义为

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;
    }
}

虽然我理解这违反了严格的Enums原则,因为与Enum相关的值是静态的(在应用程序的持续时间内),它可能是两个邪恶中的较小者。

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

在代码中访问该类会使它更难以理解(除非我遗漏了什么?)

3 个答案:

答案 0 :(得分:13)

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

然而,它们可以具有构造函数,方法和字段。 它们还可以在主体中具有在单个枚举条目中覆盖的基本方法实现。

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());
    }
}

输出:

  

形状:方形,面积:100.00
  形状:RECTANGLE,面积:150.00
  形状:CIRCLE,面积:78.54
  形状:OVAL,面积:117.81


处理编译时限制

枚举是编译时常量,因此您无法使用非常量值初始化它们,这意味着您无法执行此操作:

SQUARE(SomeClass.getSquareValue())

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

  1. 从属性文件

    自动生成枚举

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

    ENUMNAME=property.value
    

    进入此枚举条目:

    ENUMNAME("property.value")
    

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

  2. 懒惰初始化枚举(argh)

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

  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)
             );
         }
     }
    

答案 1 :(得分:2)

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

答案 2 :(得分:0)

如果你想要一个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();
    }
}