0

I am attempting to use an enum to store ID values that I can use in a switch statement later. However despite the fact the id value is stored as final byte and that you cannot add new enum entries at runtime, the compiler seems unable to realise the values are constant and thus gives the error constant expression required. Enums, by definition, are data types for storing predefined constants and yet it seems I cannot use them as such. Am I doing something unusual to warrant this strange behaviour or are Java enums not as robust as they perhaps should be?

public class Animal {
    enum AnimalClasses {
        MAMMAL((byte)0x00),
        FISH((byte)0x01),
        REPTILE((byte)0x02),
        AMPHIBIAN((byte)0x03),
        BIRD((byte)0x04);

        public final byte id;
        private AnimalClasses(byte id) {
            this.id = id;
        }
    }

    public short animalID;

    public void updateAnimal() {
        //Animal ID contains class ID encoded within
        byte animalClassID = (byte) (animalID & 0xFF);

        switch (animalClassID) {
            case AnimalClasses.MAMMAL.id:   //ERROR: Constant expression required
                //Do mammal things
                break;
            case AnimalClasses.FISH.id:     //ERROR: Constant expression required
                //Do fish things
                break;
            //...
            default:
                break;
        }
    }
}
tupto
  • 135
  • 2
  • 6
  • 1
    Those enum properties are treated like normal class properties, i.e. even though they are declared final they _could_ be changed at runtime (e.g. using reflection). – Thomas Jul 11 '22 at 11:32
  • 3
    They are not compile-time constants, as enums are runtime objects which are instantiated and capable of being mutated. With reflection, you could change the values of those fields that are `final`. That said, you can switch on the enum constants themselves (`switch (someConst) { case AnimalClasses.MAMMAL: ... }`), which can be assisted by a method within the enum like `AnimalClasses#getByID(byte id)`. – Rogue Jul 11 '22 at 11:32
  • 2
    Like many such 'why' questions, the answer is simply 'cuz'. The spec says so. Perhaps the question is best reformulated as: How do I do something like this - at that point, e.g. @Rogue's comment could be turned into an answer. – rzwitserloot Jul 11 '22 at 11:36
  • 1
    Note that an expression like `(byte) (animalID & 0xFF)` sends contradicting messages about you intention. Do you want a value between `0` and `255`, as `… & 0xFF` suggests, or do you want a value between `-128` and `127`, as produced by the type cast `(byte)…`? In the latter case, just using `byte animalClassID = (byte)animalID;` would be enough. Note that this value gets promoted to `int` anyway when using `switch (animalClassID) { … }`, so the cast is only necessary when there are actual upper bits to truncate. If `animalID` contains a value between zero and four anyway, you can just use it. – Holger Jul 11 '22 at 13:03
  • 1
    And if the `id` is always identical to the ordinal, as in your example, you can just use `Animal a = Animal.values()[(byte)animalID];` – Holger Jul 11 '22 at 13:05
  • Enums [are changeable at runtime](https://www.javaspecialists.eu/archive/Issue272-Hacking-Enums-Revisited.html) though it's a really, really bad idea to do. But it's possible, so they're not truly constant. – Kaan Jul 16 '22 at 04:14

0 Answers0