Java is incredibly smart when it comes to binary compatibility, due to the way it uses method signatures, etc, so for the most part, compatibility is easily maintained. You can add sub-classes and pass them into methods that accept their super-class, even if those methods weren't compiled with the sub-classes present, you can add methods to classes and add interfaces for them to implement, and binary compatibility will still be the same for the most part. This isn't so true for enums. Enums often use the .ordinal()
method (when compiled) to do certain tasks (see below for more bytecode stuff), so that's why you probably have to recompile when they're classes change.
Makoto's answer covers the conceptual stuff quite well, but the direct cause has to do with the bytecode generated by javac
.
The below class:
public class TestEnums {
public static void main(String[] args) throws Throwable {
Matter matter = Matter.SOLID;
switch (matter) {
case SOLID:
System.out.println("a");
break;
case LIQUID:
System.out.println("b");
break;
case GAS:
System.out.println("c");
break;
}
}
private enum Matter {
SOLID,
LIQUID,
GAS
}
}
generates the following bytecode (in the main method):
public static main([Ljava/lang/String;)V throws java/lang/Throwable
L0
LINENUMBER 27 L0
GETSTATIC TestEnums$Matter.SOLID : LTestEnums$Matter;
ASTORE 1
L1
LINENUMBER 28 L1
GETSTATIC TestEnums$1.$SwitchMap$TestEnums$Matter : [I
ALOAD 1
INVOKEVIRTUAL TestEnums$Matter.ordinal ()I
IALOAD
TABLESWITCH
1: L2
2: L3
3: L4
default: L5
L2
LINENUMBER 30 L2
FRAME APPEND [TestEnums$Matter]
GETSTATIC java/lang/System.out : Ljava/io/PrintStream;
LDC "a"
INVOKEVIRTUAL java/io/PrintStream.println (Ljava/lang/String;)V
L6
LINENUMBER 31 L6
GOTO L5
L3
LINENUMBER 33 L3
FRAME SAME
GETSTATIC java/lang/System.out : Ljava/io/PrintStream;
LDC "b"
INVOKEVIRTUAL java/io/PrintStream.println (Ljava/lang/String;)V
L7
LINENUMBER 34 L7
GOTO L5
L4
LINENUMBER 36 L4
FRAME SAME
GETSTATIC java/lang/System.out : Ljava/io/PrintStream;
LDC "c"
INVOKEVIRTUAL java/io/PrintStream.println (Ljava/lang/String;)V
L5
LINENUMBER 39 L5
FRAME SAME
RETURN
L8
LOCALVARIABLE args [Ljava/lang/String; L0 L8 0
LOCALVARIABLE matter LTestEnums$Matter; L1 L8 1
MAXSTACK = 2
MAXLOCALS = 2
getting past the verbosity of the bytecode, the main logic of the switch statement is present in the form of a TableSwitch:
INVOKEVIRTUAL TestEnums$Matter.ordinal ()I
IALOAD
TABLESWITCH
1: L2
2: L3
3: L4
default: L5
If you look, you see that the switch invokes the .ordinal() method to determine which value to go to, so if you insert a value at the start of the enum:
private enum Matter {
PLASMA, //Lying elementary school science teachers don't tell you about this one
SOLID,
LIQUID,
GAS
}
the ordinals are changed. Since bytecode uses the ordinal method a lot when it comes to manipulating enums, code may have to be recompiled to maintain compatibility with the enum.