74 votes

Enum Java et fichiers de classe supplémentaires

J'ai remarqué enums d'introduire de nombreux autres fichiers de classe (Classe$1) après la compilation des ballonnements, de la taille totale. Il semble être attachée à chaque classe qui utilise même un enum, et ce sont souvent dupliquées.

Pourquoi cela se produit-il et est-il un moyen pour éviter cela sans enlever l'enum.

(Raison de la question de l'espace est à une prime pour moi)

MODIFIER

D'examiner la question plus en détail, du Soleil Javac 1.6 donne un synthétique de la classe chaque fois que vous utilisez un commutateur sur un Enum. Il utilise une sorte de SwitchMap. Ce site a des informations un peu plus, et ici vous indique comment analyser ce que Javac est en train de faire.

Un supplément de fichier physique semble un prix élevé à payer chaque fois que vous utilisez un commutateur sur un enum!

Il est intéressant de noter, Eclipe le compilateur de ne pas produire ces fichiers supplémentaires. Je me demande si la seule solution est de changer les compilateurs?

67voto

John Kugelman Points 108754

J'étais juste un peu par ce comportement et cette question a montré lors de Googler. J'ai pensé que je devais partager le peu d'informations supplémentaires, je l'ai découvert.

javac 1.5 et 1.6 créer un nouveau synthétique de la classe chaque fois que vous utilisez un commutateur sur un enum. La classe contient un soi-disant "changer de carte" google maps enum indices pour passer de la table de saut numéros. Surtout, le synthétique, la classe est créée pour la classe dans laquelle la commutation se produit, pas la classe enum.

Voici un exemple de ce qui est généré:

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

Synthétique 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;
    };
}

Ce commutateur de la carte est ensuite utilisé pour générer un index pour un lookupswitch ou tableswitch de la JVM de l'instruction. Il convertit chaque valeur d'enum dans un index correspondant de 1 à [numéro de l'interrupteur d'affaires].

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 est utilisé s'il y a trois ou plus de l'interrupteur cas qu'il effectue plus efficace constante de temps de recherche vs lookupswitch's la recherche linéaire. Techniquement parlant javac pourrait omettre toute cette affaire avec le synthétique de changer de carte quand il utilise lookupswitch.

La spéculation: je n'ai pas l'Éclipse du compilateur sous la main pour tester, mais j'imagine que ça ne dérange pas avec une synthèse de la classe et utilise simplement lookupswitch. Ou peut-être qu'il exige plus de commutateur de cas que l'original asker testé avec avant de "ugprades" pour tableswitch.

11voto

TDJoe Points 467

Je crois que ceci est fait pour empêcher les commutateurs de la rupture si la commande de l'enum est changé, tandis que pas la recompilation de la classe avec le commutateur. Considérons le cas suivant:

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

Sans changer de carte, foo() serait à peu près traduire:

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

Depuis l'arrêt des déclarations doivent être des constantes de compilation (par exemple, ne pas les appels de méthode). Dans ce cas, si la commande de l' A est sous tension, foo() serait d'imprimer "Un" pour les DEUX, et vice versa.

7voto

Michael Borgwardt Points 181658

Le $1 etc. les fichiers se produire lorsque vous utilisez le "par exemple la méthode de la mise en œuvre" de la fonctionnalité de Java enums, comme ceci:

public enum Foo{
    YEA{
        public void foo(){ return true };
    },
    NAY{
        public void foo(){ return false };
    };

    public abstract boolean foo();
}

Le ci-dessus va créer trois fichiers de classe, un pour la base enum classe et un pour OUI et NON pour contenir les différentes implémentations de foo().

Sur le pseudo-code de niveau, les énumérations sont simplement des classes, et pour chaque enum exemple pour mettre en œuvre une méthode différemment, il doit y avoir un classe différente pour chaque instance,

Toutefois, cela ne tient pas compte de classe supplémentaire fichiers générés pour les utilisateurs de l'enum, et je soupçonne que ce ne sont que le résultat de classes anonymes et n'ont rien à voir avec les énumérations.

Ainsi, afin d'éviter de telles catégorie "extra" les fichiers générés, ne pas utiliser par exemple la méthode de mise en œuvre. Dans des cas comme celui-ci, où les méthodes de retour constantes, vous pouvez utiliser un public final terrain situé dans un constructeur au lieu (ou un domaine privé avec un public de lecture si vous préférez). Si vous avez vraiment besoin de méthodes avec des logiques différentes pour les différents enum cas, alors vous ne pouvez pas éviter les cours supplémentaires, mais je considère que c'est un plutôt exotique et rarement besoin d'une fonctionnalité.

1voto

vicjugador Points 2220

En Java, les énumérations ne sont en réalité que des classes avec du sucre syntaxique jeté.

Ainsi, chaque fois que vous définissez une nouvelle énumération, le compilateur Java crée un fichier de classe correspondant. (Peu importe la simplicité de l'énumération).

Pas moyen de contourner cela, à part ne pas utiliser d'énumérations.

Si l'espace est une prime, vous pouvez toujours utiliser des constantes à la place.

-2voto

dfa Points 54490

pour autant que je sache, avec une énumération nommée Operation vous obtiendrez des fichiers de classe supplémentaires, à l’exception de la valeur évidente Operation.class , et d’une valeur par enum, si vous utilisez abstract method comme celui-ci:

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

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X