Reputation: 3596
I have the following Enum constants for real life equipment:
HELMET,
CHESTPIECE,
BOOTS,
SWORD,
MACE,
HAMMER,
SHIELD,
BOW,
CROSSBOW,
STAFF
...;
I have another class called Battle
which dictates what equipment can be used in that specific battle. For example:
new Battle(Equipment.HAMMER, Equipment.SHIELD, EQUIPMENT.BOW);
Which means that only Hammers, Shields, or Bows can be used.
Now I expanded on that and have the need for sub categories. For example:
new Battle(Equipment.SHIELD, Equipment.Weapons.values())
Which is equivalent to saying:
new Battle(Equipment.SHIELD, Equipment.SWORD, Equipment.MACE, Equipment.HAMMER, ...)
etc
Which also means that new Battle(Equipment.values())
should yield every enum value
Since Enum
s are final, I tried the following:
public interface Equipment { }
public enum MeleeWeapon implements Equipment
{
SWORD,
MACE,
HAMMER,
STAFF, ...;
}
public enum RangedWeapon implements Equipment
{
BOW, CROSSBOW;
}
...
But with this, I'm unable to say Equipment.Weapon.values() // get all weapons, ranged and melee
. There's no sense of inherited relationships between classes, and I also lose everything that is not defined in the interface. It doesn't feel like a good solution here.
I tried making regular classes:
public abstract class Equipment
{
private static Set<Equipment> instances = new HashSet<>();
public static Set<Equipment> values()
{
return instances;
}
public Equipment()
{
instances.add(this);
}
}
public abstract class Weapon extends Equipment
{
private static Set<Weapon> instances = new HashSet<>();
public static Set<Weapon> values()
{
return instances;
}
public Weapon()
{
super() // explicit call
instances.add(this);
}
}
public class MeleeWeapon extends Weapon
{
private static Set<MeleeWeapon> instances = new HashSet<>();
public static final MeleeWeapon SWORD = new MeleeWeapon();
public static final MeleeWeapon MACE = new MeleeWeapon();
...
public static Set<MeleeWeapon> values()
{
return instances;
}
public MeleeWeapon()
{
super() // explicit call
instances.add(this);
}
}
Unfortunately there is a ton of repeated code, heavy on memory, and also public static Set<Weapon> values()
causes a compile error because it attempts to override values()
in the superclass with a different return type. I was able to solve this with generics (<? extends Weapon>
) but it's still an awful solution.
What is the right approach here? I need inheritance with my enum values but I cannot find a way how to do so.
Upvotes: 1
Views: 71
Reputation: 3964
Still keeping the enum usage, it is possible to associate each element of the enumeration with the groups to which it belongs and then return filtered groups of elements in dedicated methods.
We'll need another - smaller - enum which enumerates the properties to filter on, for example:
public enum EquipmentType {
WEAPON, ARMOR, TOOL, CLOTHING;
}
The elements of the enumeration are associated with their respective groups:
public enum Equipment {
HELMET(ARMOR),
CHESTPIECE(ARMOR),
BOOTS(ARMOR, CLOTHING),
SWORD(WEAPON),
MACE(WEAPON),
HAMMER(WEAPON, TOOL),
SHIELD(ARMOR),
BOW(WEAPON),
CROSSBOW(WEAPON),
STAFF(WEAPON);
private final Set<EquipmentType> types;
Equipment(EquipmentType... eqTypes) {
this.types = Arrays.stream(eqTypes)
.collect(Collectors.toSet());
}
// common filtering method
private static List<Equipment> filterByType(EquipmentType type) {
return Arrays.stream(values())
.filter(eq -> eq.types.contains(type))
.collect(Collectors.toList());
}
// dedicated methods for each group of items
public static List<Equipment> getWeapons() {
return filterByType(WEAPON);
}
public static List<Equipment> getArmor() {
return filterByType(ARMOR);
}
}
There is still no inheritance or more evolved typing involved in this approach and I think it would be better to avoid using the enum at all if you want more flexibility.
Upvotes: 3