Search code examples
javagenericsbuilder

How to reference enum fields from within Builder of another class?


I'm using a Builder pattern to create Item objects in my game, and I want to be able to use item prefixes and suffixes to generate random Items, as in Item.builder().random().build(). Prefix and Suffix are both enums with ragged fields. All of them have a name, but some have damage and luck fields, for example. How do I access the enums' fields from within the builder?

I have public methods in each enum that return a random Prefix and Suffix, my Item Builder's randomize method calls these and stores the affixes locally. But I can't seem to get at their member variables via reflection. prefix.getClass().getDeclaredField("name").toString returns java.lang.String roan.ItemPrefix$2.name instead of it's actual name. Plus it seems hacky to me.

Enum:

enum Prefix {
    ARROGANT {
        String name = "Arrogant ";
        int damage = 20;
        int luck = 2;
    },
    BLOODY {
        String name = "Bloody ";
        int damage = 30;
    },
    CURIOUS {
        String name = "Curious ";
        int luck = 4;
    },

    private static final int size = Prefix.values().length;

    public static Prefix randomPrefix() {
        return Prefix.values()[Dice.roll(Prefix.size)];
    }
}

Builder:

public static abstract class Builder<T extends Builder<T>> {
    // Default values
    private String name = "itemName"; 
    private int damage = 5;
    private int luck = 0;  

    protected abstract T self();

    public T name(String name) {
        this.name = name;
        return self();
    }

    // More builder methods
    ...

    // PROBLEMATIC CODE
    public T random() {
        Prefix prefix = Prefix.randomPrefix();
        this.name = prefix.getClass().getDeclaredField("name").toString();
        return self();
        }

        public Item build() {
            return new Item(this);
        }
    }
}

// Builder helper classes
...

Ideally it would assemble a fully-formed items using the member fields of the enums passed to it. I'm using enum Prefix rather than an array of Prefix objects because I want to use certain EnumSets elsewhere for subclasses of Item like Armor, which can only use certain prefixes.


Solution

  • Your current design depends on 3 anonymous subclasses of the enum Prefix. This is not necessary. Rather try this:

    enum Prefix {
      ARROGANT ("Arrogant ",20,2), BLOODY("Bloody ",30,0), CURIOUS("Curious ",5, 4);
    
      public String name = "itemName"; 
      public int damage = 5;
      public int luck = 0;  
    
      Prefix(String name, int damage, int luck) {
        this.name = name;
        this.damage = damage;
        this.luck = luck;
      }
    
      ...
    }
    

    Consequently you will not need reflection to access your fields.

    If you really want the alternatives arrogant, bloody, curious to have different schemas, the decision for an enum is wrong. Rather select random in a list of factories, each returning the completely correct configured item for this random selection.