i read Josh Bloch's "Effective Java" atm. And i dont understand these phrases: Programs that use int enums are brittle. Because int enums are constant variables [JLS, 4.12.4], their int values are compiled into the clients that use them [JLS, 13.1]. If the value associated with an int enum is changed, its clients must be recompiled. If not, the clients will still run, but their behavior will be incorrect. Mostly checked text. Can somebody explain it for me in other words?
CodePudding user response:
int
and other primitives are stored as values and not as references that would be used for a proper enum value. Now imagine you're using an int
constant with value 5
somehwere and the compiler decides to inline it, i.e. it replaces the access to the constant with the value.
An an example assume the following line somewhere in client code:
static int x = Constants.intEnum; //intEnum has value 5
If the compiler inlines it, the byte code would basically resemble this:
static int x = 5; //value is resolved at compile time which is ok because it is constant
Now imagine you'd change intEnum
to have a value of 7
. If you don't also recompile the code above the byte code would still contain the x = 5
equivalent. Only recompilation of the client would cause this to be updated to x = 7
- and knowing and recompiling all clients of a library might not be so easy or even desirable.
On the other hand, access to a proper enum wouldn't be inlined, i.e. the following would still be the same:
static MyEnum x = MyEnum.FIVE; //This would be the same as Constants.intEnum
Now if you'd change your library and replace FIVE
with SEVEN
and the client wouldn't be recompiled you should get an exception at runtime telling you that enum value FIVE
cannot be found.
Also, if your enum would have a value and a getter for it, e.g. int getSomeValue()
then the following should also stay the same, i.e. not be inlined (I'd need to confirm this, so if anyone has proof for or against this, please share in the commets):
static int x = MyEnum.THE_VALUE.getSomeValue(); //may return 5 or 7 at runtime
Now the call to getSomeValue()
should be resolved at runtime so if you change from 5
to 7
you'd not need to recompile the client.
CodePudding user response:
There are two aspects to this:
- The
int enums
he's writing about are not 'real enums' like Javaenum
s, but only int values that are defined in / matched with int constants.
- In Java, enums are class-like things. In almost all aspects, you can implement a Java enum just like a class, with minor restrictions (private constructor, no inheritance)
- You can also use Java's enum variables like you would use Object variables, with the same operators etc
- The classical 'int enums' that other languages use will usually not be treated like classes/objects, but - in Java terms - primitives. So this gives the whole 'enum' handling a very different aspect from Java
- The problem that he describes ("If the value associated with an int enum is changed, its clients must be recompiled. If not, the clients will still run, but their behavior will be incorrect.") still holds true for some situations concerning Java enums.
- If you try to store those, you usually(wrongfully) reduce their storage value to an int, the
ordinal()
value. However, if you change the order of enums by adding/removing/reordering enum constants inside the enum class, their respectiveordinal()
value will also change. If you then restore an enum from data, you load the old ordinal value but will get the wrong enum constant. So you should always have an individual enum ID in your Java enums, or save and restore by the enum constants' names. This is just a Java analogy to what can happen with other languages when updating libraries. - In Java you can also run into problems with enums and updating libraries. But it will not use a wrong enum silently, rather throw a runtime error and inform you that the enum you tried to address does not exist anymore.