Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Implicitly Inner Class Created in Java Enum After Compiling [duplicate]

Tags:

java

enums

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?

like image 735
Pool Avatar asked Dec 02 '09 17:12

Pool


People also ask

Is it possible to define an enum as an inner class?

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.

What is the use of inner class in Java?

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.

Can you have a static class inside a nested enum type?

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.

How to declare an inner class within an outer class?

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.


5 Answers

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.

like image 174
John Kugelman Avatar answered Nov 07 '22 16:11

John Kugelman


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.

like image 31
TDJoe Avatar answered Nov 07 '22 16:11

TDJoe


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.

like image 35
Michael Borgwardt Avatar answered Nov 07 '22 14:11

Michael Borgwardt


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.

like image 29
vicsz Avatar answered Nov 07 '22 15:11

vicsz


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.

like image 43
Chandra Sekhar Avatar answered Nov 07 '22 16:11

Chandra Sekhar