Java enum and additional class files

JavaEnums

Java Problem Overview


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. http://jakeherringbone.com/2009/04/switchmap/">This</a> site has some more information, and https://stackoverflow.com/questions/1163076/how-is-values-implemented-for-java-6-enums/1163121#1163121">here</a> 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?

Java Solutions


Solution 1 - Java

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:

EnumClass.java
public enum EnumClass { VALUE1, VALUE2, VALUE3 }
EnumUser.java
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";
        }
    }
}
Synthetic EnumUser$1.class
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].

EnumUser.class
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.

Solution 2 - Java

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.

Solution 3 - Java

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.

Solution 4 - Java

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.

Solution 5 - Java

Considering this behaviour of Java is not known to all Java developers, I created few videos explaining how Switch statements in Java works.

  1. Switch with Enums - https://www.youtube.com/watch?v=HlsPHEB_xz4
  2. Switch with Strings - https://www.youtube.com/watch?v=cg9O815FeWY
  3. About TableSwitch and LookupSwitch - https://www.youtube.com/watch?v=OHwDczHbPcw
  4. Switch Expression in Java 13 - https://www.youtube.com/watch?v=suFn87Irpb4

This might not answer the question in a straight way. However, it does definitely answers how the switch statements in Java works.

Solution 6 - Java

as far I know, given an enum named Operation you will get additional class files, excluding the obvious Operation.class, and one per enum value, if you are using abstract method like this one:

enum Operation {

   ADD {
      double op(double a, double b) { 
          return a + b;
      }
   },

   SUB {
      double op(double a, double b) { 
          return a - b;
      }
   };

   abstract double op(double a, double b);
}

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionPoolView Question on Stackoverflow
Solution 1 - JavaJohn KugelmanView Answer on Stackoverflow
Solution 2 - JavaTDJoeView Answer on Stackoverflow
Solution 3 - JavaMichael BorgwardtView Answer on Stackoverflow
Solution 4 - JavavicszView Answer on Stackoverflow
Solution 5 - JavaChandra SekharView Answer on Stackoverflow
Solution 6 - JavadfaView Answer on Stackoverflow