I've noticed enums
introduce many additional class files (Class$1) after compilation bloating the total size. It seems to be attached to every class that even uses an enum, and these are often duplicated.
Why does this occur and is there a way to prevent this without removing the enum.
(Reason for question is space is at a premium for me)
EDIT
On investigating the issue further, Sun's Javac 1.6 creates an additional synthetic class each time you use a switch on an Enum. It uses some kind of SwitchMap. This site has some more information, and here tells you how to analyse what Javac is doing.
An additional physical file seems a high price to pay each time you use a switch on an enum!
Interestingly, Eclipe's compiler does not produce these additional files. I wonder if the only solution is to switch compilers?
Inner Classes and Enclosing Instances ), and since the enum synthetic class does, it makes it unacceptable as inner class. Show activity on this post. Already enough information from +Joachim Sauer, I am just adding some extra details. You can define inner enum only if your inner class is static nested inner class.
Inner Classes (Non-static Nested Classes) Inner classes are a security mechanism in Java. We know a class cannot be associated with the access modifier private, but if we have the class as a member of other class, then the inner class can be made private. And this is also used to access the private members of a class.
enum types that are defined as nested types are always implicitly static (see JLS §8.9. Enums) You can't have a static nested type inside a non-static one (a.k.a an "inner class", see JLS §8.1.3.
Inner class can be declared within a method of an outer class which we will be illustrating in the below example where Inner is an inner class in outerMethod (). Method Local inner classes can’t use a local variable of the outer method until that local variable is not declared as final. For example, the following code generates a compiler error.
I was just bit by this behavior and this question showed up when Googling. I thought I'd share the little bit of extra information I found out.
javac 1.5 and 1.6 create an additional synthetic class each time you use a switch on an enum. The class contains a so-called "switch map" which maps enum indices to switch table jump numbers. Importantly, the synthetic class is created for the class in which the switch occurs, not the enum class.
Here's an example of what gets generated:
public enum EnumClass { VALUE1, VALUE2, VALUE3 }
public class EnumUser {
public String getName(EnumClass value) {
switch (value) {
case VALUE1: return "value 1";
// No VALUE2 case.
case VALUE3: return "value 3";
default: return "other";
}
}
}
class EnumUser$1 {
static final int[] $SwitchMap$EnumClass = new int[EnumClass.values().length];
static {
$SwitchMap$EnumClass[EnumClass.VALUE1.ordinal()] = 1;
$SwitchMap$EnumClass[EnumClass.VALUE3.ordinal()] = 2;
};
}
This switch map is then used to generate an index for a lookupswitch
or tableswitch
JVM instruction. It converts each enum value into a corresponding index from 1 to [number of switch cases].
public java.lang.String getName(EnumClass);
Code:
0: getstatic #2; //Field EnumUser$1.$SwitchMap$EnumClass:[I
3: aload_1
4: invokevirtual #3; //Method EnumClass.ordinal:()I
7: iaload
8: lookupswitch{ //2
1: 36;
2: 39;
default: 42 }
36: ldc #4; //String value 1
38: areturn
39: ldc #5; //String value 3
41: areturn
42: ldc #6; //String other
44: areturn
tableswitch
is used if there are three or more switch cases as it performs a more efficient constant-time lookup vs. lookupswitch
's linear search. Technically speaking javac could omit this whole business with the synthetic switch map when it uses lookupswitch
.
Speculation: I don't have Eclipse's compiler on hand to test with but I imagine that it doesn't bother with a synthetic class and simply uses lookupswitch
. Or perhaps it requires more switch cases than the original asker tested with before it "ugprades" to tableswitch
.
I believe this is done to prevent switches from breaking if the ordering of the enum is changed, while not recompiling the class with the switch. Consider the following case:
enum A{
ONE, //ordinal 0
TWO; //ordinal 1
}
class B{
void foo(A a){
switch(a){
case ONE:
System.out.println("One");
break;
case TWO:
System.out.println("Two");
break;
}
}
}
Without the switch map, foo()
would roughly translate to:
void foo(A a){
switch(a.ordinal()){
case 0: //ONE.ordinal()
System.out.println("One");
break;
case 1: //TWO.ordinal()
System.out.println("Two");
break;
}
}
Since case statements must be compile-time constants (e.g. not method calls). In this case, if the ordering of A
is switched, foo()
would print out "One" for TWO, and vice versa.
The $1 etc. files occur when you use the "per-instance method implementation" feature of Java's enums, like this:
public enum Foo{
YEA{
public void foo(){ return true };
},
NAY{
public void foo(){ return false };
};
public abstract boolean foo();
}
The above will create three class files, one for the base enum class and one each for YEA and NAY to hold the different implementations of foo().
On the bytecode level, enums are just classes, and in order for each enum instance to implement a method differently, there needs to be a different class for each instance,
However, this does not account for additional class files generated for users of the enum, and I suspect those are just the result of anonymous classes and have nothing to do with enums.
Thus, in order to avoid such extra class files to be generated, do not use per-instance method implementations. In cases such as above where the methods return constants, you can use a public final field set in a constructor instead (or a private field with a public getter if you prefer). If you really need methods with different logic for different enum instances, then you can't avoid the extra classes, but I'd consider it a rather exotic and rarely needed feature.
In Java, Enumerations are really just Classes with some syntactic sugar thrown on.
So anytime you define a new Enumeration, the Java compiler will create a corresponding Class file for you. (No matter how simple the Enumeration is).
No way to get around this, other then not using Enumerations.
If space is a premium, you can always just use Constants instead.
Considering this behaviour of Java is not known to all Java developers, I created few videos explaining how Switch statements in Java works.
This might not answer the question in a straight way. However, it does definitely answers how the switch statements in Java works.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With