2


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

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

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

4

5 回答 5

2

您可以enum实现一个接口。

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


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

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

于 2012-08-31T20:57:18.257 回答
1

嗯,Java 中的枚举只是简单的类,语言保证已知对象的集合在编译时是已知的并受到限制。如果你想在运行时添加新的枚举文字,你最终会得到常规类。

于 2012-08-31T20:54:19.083 回答
1

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

enum Season { WINTER, SPRING, SUMMER, AUTUMN }

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

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

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

于 2012-08-31T21:18:42.137 回答
1

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

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

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

现在一个enum, DefaultTerrain:

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. 当修改者添加一个新的Terrain时,他们只需要实现Terrain并注册它TerrainManager

static初始化程序确保在添加任何其他内容之前添加您的对象DefaultTerrain。如果您使用单例,则可以将其放入类构造函数中。

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

于 2012-08-31T21:24:53.807 回答
0

您将需要 CGLIB,http://cglib.sourceforge.net/

于 2012-08-31T20:53:19.273 回答