如何模拟可以在Java中运行时扩展的枚举?

时间:2012-08-31 20:50:39

标签: java class enums


我正在编写一个程序来模拟一个名为“文明4”的游戏中的城市。为了做到这一点,我有几个枚举来表示该城市拥有的每个地块的地形,资源,改进等类型。

问题是我想编程与Fan制作的mod兼容,这可能会为游戏添加需要被我的独立实用程序接受的东西。所以我想创建一个Enum style 类来保存由加载的mods定义的新类型(因为在运行时无法更改枚举),这是在用户输入要加载的mod时在运行时创建的(这是一个解析为读取新添加的txt文件)

那么有没有办法模拟在运行时创建并添加到 的枚举?我认为静态成员变量不能用,因为它们是在运行时之前完成的......

5 个答案:

答案 0 :(得分:2)

您可以enum实现接口。

这样您可以在枚举中定义您的定义值,但新值可以是实现接口的任何类。


另一种方法是使用字节代码生成器或编译器API在运行时生成或加载enum。我写了一个库,以便更容易获取String并编译和加载它。

http://vanillajava.blogspot.co.uk/2010_11_01_archive.html

答案 1 :(得分:1)

嗯,Java中的枚举只是一种类,其中语言保证已知对象的集合在编译时是已知的并且是有限的。如果要在运行时添加新的枚举文字,最终会得到常规类。

答案 2 :(得分:1)

枚举的美妙之处在于,您可以在代码中编写人类可读的名称,这些代码在幕后编译为数字,因为数字之类的计算机更好。以这个枚举为例:

enum Season { WINTER, SPRING, SUMMER, AUTUMN }

在幕后WINTER可能是0(零),SPRING是1等。 要在运行时代码中复制此行为,您可以创建一个字符串列表,如下所示:

List<String> seasons;
seasons = new ArrayList<String>();
seasons.add("Winter");
seasons.add("Spring");
...

通过这种方式,您可以将项目引用为数字,例如season [1]将等于“Spring”。 这个答案只是解决这个问题的众多方法之一。

答案 3 :(得分:1)

默认情况下,enum类型只有一定数量的值。 enum类型中的值实际上声明为static final,并且无法在运行时添加更多内容。

话虽如此,您可以使用其他模式来实现您想要的效果。我们来看看使用接口和注册系统。我们将从Terrain界面开始:

public interface Terrain {
    int getId();
    String getName();
    int getSightBonus();
}

现在是enumDefaultTerrain

public enum DefaultTerrain implements Terrain {
    PLAINS(0, "Plains", 1),
    HILLS(1, "Hills", -1),
    MOUNTAINS(2, "Mountains", -2);

    private int id;
    private String name;
    private int sightBonus;

    private DefaultTerrain(int id, String name, int sightBonus) {
        this.id = id;
        this.name = name;
        this.sightBonus = sightBonus;
    }

    public int getId() {return id;}

    public String getName() {return name;}

    public int getSightBonus() {return sightBonus;}
}

注册类,可以是静态实用程序类,也可以是单例。

public class TerrainManager {
    private static Map<Integer, Terrain> terrainsById = new HashMap<>();

    static {
        for (DefaultTerrain terrain : DefaultTerrain.values())
            register(terrain);
    }

    public static void register(Terrain terrain) {
        Integer id = terrain.getId();
        if (terrainsById.contains(terrain.getId()))
            throw new IllegalArgumentException("Terrain with id already exists: " + id);
        terrainsById.put(id, terrain);
    }

    public static Terrain getTerrainById(int id) {
        return terrainsById.get(id);
    }

    public static Set<Terrain> getAllTerrains() {
        return new HashSet<Terrain>(terrainsById.values());
    }
}

这最后一堂课是魔术发生的地方。据推测,模仿者会在游戏的世界定义中使用某种标识符来说“使用这个图块”,对吧?在这种情况下,我将其称为整数id,但实际上它可以是任何类型,只需相应地修改Map即可。在地图加载代码中,只需使用世界定义中的ID即可查找Terrain。当modder添加新的Terrain时,他们只需要实现Terrain并将其注册到TerrainManager

static初始值设定项可确保在添加任何其他内容之前添加DefaultTerrain个对象。如果使用单例,则可以将其放入类构造函数中。

将此模式用于您希望用户添加到的不同enum类型。除了enum之外,您还可以将它用于几乎任何其他类型。

答案 4 :(得分:0)