簡體   English   中英

如何模擬可以在Java運行時擴展的枚舉?

[英]How can I simulate Enums that can be expanded at Runtime in Java?


我正在編寫一個程序,從一個名為《文明4》的游戲中模擬城市。為此,我使用了多個枚舉來表示該城市擁有的每個地塊的地形類型,資源,改善情況等。

問題是我想編程以與Fan made mods兼容,這可能會增加游戲中需要被我獨立的工具接受的東西。 所以我想到了創建一個Enum 樣式類來容納由加載的mod定義的新類型(因為Enums在運行時無法更改),該類型是在運行時在用戶輸入要加載的mod時創建的(這是一個txt文件,被解析以讀取新添加的內容)

那么,有沒有一種方法可以模擬在運行時創建並添加到的枚舉? 我認為靜態成員變量無法使用,因為它們在運行時之前就已完成...

您可以使enum實現接口。

這樣,您可以在枚舉中包含定義的值,但是新值可以是實現該接口的任何類。


另一種選擇是在運行時使用字節碼生成器或Compiler API生成或加載enum 我編寫了一個庫,以使其更容易采用String並進行編譯和加載。

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

嗯,Java中的枚舉只是類,在該類中,語言保證了已知對象的集合在編譯時是已知的並且受到限制。 如果要在運行時添加新的枚舉常量,則需要使用常規類。

枚舉的優點在於,您可以在代碼中編寫人類可讀的名稱,這些名稱在幕后編譯為數字,因為計算機更喜歡數字。 以這個枚舉為例:

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”。 這個答案只是解決這個問題的多種方式之一。

默認情況下, 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 當修改器添加新的Terrain ,他們只需要實現Terrain並將其注冊到TerrainManager

static初始化程序確保在添加任何其他內容之前先添加DefaultTerrain對象。 如果使用單例,則可以將其放入類構造函數中。

將此模式用於要用戶添加到的不同enum類型。 除了enum您還可以將其用於幾乎所有其他類型。

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

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM