programing

Java enum 및 추가 클래스 파일

nasanasas 2020. 11. 25. 08:02
반응형

Java enum 및 추가 클래스 파일


enums컴파일이 전체 크기를 크게 늘린 후 많은 추가 클래스 파일 (Class $ 1)이 도입 된 것을 확인 했습니다. 열거 형을 사용하는 모든 클래스에 연결된 것처럼 보이며 종종 중복됩니다.

왜 이것이 발생하며 열거 형을 제거하지 않고이를 방지 할 수있는 방법이 있습니다.

(질문의 이유는 공간이 나에게 중요하다는 것입니다)

편집하다

문제를 더 조사 할 때 Sun의 Javac 1.6은 Enum에서 스위치를 사용할 때마다 추가 합성 클래스를 만듭니다 . 일종의 SwitchMap을 사용합니다. 사이트에는 더 많은 정보가 있으며 여기 에서는 Javac이 수행하는 작업을 분석하는 방법을 알려줍니다.

추가 실제 파일은 열거 형에서 스위치를 사용할 때마다 지불해야하는 높은 비용으로 보입니다!

흥미롭게도 Eclipe의 컴파일러는 이러한 추가 파일을 생성하지 않습니다. 유일한 해결책은 컴파일러를 전환하는 것입니까?


나는이 행동에 조금이라도 있었고이 질문은 인터넷 검색을 할 때 나타났습니다. 나는 내가 찾은 약간의 추가 정보를 공유 할 것이라고 생각했다.

javac 1.5 및 1.6은 열거 형에서 스위치를 사용할 때마다 추가 합성 클래스를 만듭니다. 이 클래스에는 테이블 점프 번호를 전환하기 위해 열거 형 인덱스를 매핑하는 소위 "스위치 맵"이 포함되어 있습니다. 중요한 것은 enum 클래스 아니라 스위치가 발생하는 클래스에 대해 합성 클래스가 생성 된다는 것입니다.

다음은 생성되는 항목의 예입니다.

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";
        }
    }
}

합성 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;
    };
}

그런 다음이 스위치 맵을 사용하여 lookupswitch또는 tableswitchJVM 명령에 대한 색인을 생성합니다 . 각 열거 형 값을 1에서 [스위치 케이스 수]까지의 해당 인덱스로 변환합니다.

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

tableswitchlookupswitch의 선형 검색에 비해보다 효율적인 일정 시간 조회를 수행하므로 스위치 케이스가 3 개 이상인 경우 사용됩니다 . 기술적으로 말하면 javac는 .NET을 사용할 때 합성 스위치 맵을 사용하여 전체 비즈니스를 생략 할 수 lookupswitch있습니다.

추측 : 테스트 할 Eclipse 컴파일러는 없지만 합성 클래스를 사용하지 않고 단순히 lookupswitch. 또는 "ugprades"로 변경하기 전에 테스트 한 원래 asker보다 더 많은 스위치 케이스가 필요합니다 tableswitch.


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.


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.


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.


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);
}

참고URL : https://stackoverflow.com/questions/1834632/java-enum-and-additional-class-files

반응형