Comment les valeurs () sont-elles implémentées pour les énumérations Java 6?

En Java, vous pouvez créer un enum comme suit:

public enum Letter { A, B, C, D, E, F, G; static { for(Letter letter : values()) { // do something with letter } } } 

Cette question concerne la méthode “values ​​()”. Plus précisément, comment est-il mis en œuvre? Habituellement, je pouvais passer directement à la source pour les classes Java en utilisant F3 ou CTRL + Clic dans Eclipse (même pour les classes comme Ssortingng, Character, Integer et même Enum). Il est possible d’afficher la source des autres méthodes enum (par exemple, valueOf (Ssortingng)).

Est-ce que “values ​​()” crée un nouveau tableau chaque fois qu’il est appelé? Si je l’assigne à une variable locale, puis modifie l’un des éléments, que se passe-t-il (cela n’affecte clairement pas la valeur renvoyée par values ​​(), ce qui implique qu’un nouveau tableau est alloué à chaque fois).

Le code est-il natif? Ou bien le JVM / compilateur le traite-t-il spécialement, ne renvoyant qu’une nouvelle instance de values ​​() lorsqu’il ne peut pas prouver qu’il ne sera pas modifié.

Fondamentalement, le compilateur (javac) traduit votre enum dans un tableau statique contenant toutes vos valeurs au moment de la compilation. Lorsque vous appelez des valeurs (), vous obtenez une copie .clone’d () de ce tableau.

Compte tenu de cette simple énumération:

 public enum Stuff { COW, POTATO, MOUSE; } 

Vous pouvez réellement regarder le code généré par Java:

 public enum Stuff extends Enum { /*public static final*/ COW /* = new Stuff("COW", 0) */, /*public static final*/ POTATO /* = new Stuff("POTATO", 1) */, /*public static final*/ MOUSE /* = new Stuff("MOUSE", 2) */; /*synthetic*/ private static final Stuff[] $VALUES = new Stuff[]{Stuff.COW, Stuff.POTATO, Stuff.MOUSE}; public static Stuff[] values() { return (Stuff[])$VALUES.clone(); } public static Stuff valueOf(Ssortingng name) { return (Stuff)Enum.valueOf(Stuff.class, name); } private Stuff(/*synthetic*/ Ssortingng $enum$name, /*synthetic*/ int $enum$ordinal) { super($enum$name, $enum$ordinal); } } 

Vous pouvez voir comment javac «traduit» vos classes en créant un répertoire temporaire et en exécutant:

 javac -d  -XD-printflat filename.java 

Si vous l’assignez à une variable locale, la seule chose que vous pouvez modifier est d’affecter un autre enum à cette variable. Cela ne changera pas l’enum lui-même car vous ne faites que modifier l’object de vos références de variable.

Il semble que les énumérations soient en fait des singletons, de sorte qu’un seul élément de chaque enum puisse exister dans votre programme entier, ce qui rend l’opérateur == légal pour les énumérations.

Il n’y a donc pas de problème de performance et vous ne pouvez pas modifier accidentellement quelque chose dans votre définition d’énumération.

Le code est-il natif? Ou bien le JVM / compilateur le traite-t-il spécialement, ne renvoyant qu’une nouvelle instance de values ​​() lorsqu’il ne peut pas prouver qu’il ne sera pas modifié.

1) Non. Ou du moins pas dans les implémentations actuelles. Voir la réponse de @ lucasmo pour la preuve.

2) AFAIK, non.

Hypothétiquement, cela pourrait le faire. Cependant, prouver qu’un tableau n’est jamais modifié localement serait compliqué et relativement coûteux pour le JIT. Si le tableau “échappe” à la méthode appelée values() , il devient plus complexe et plus coûteux.

Les chances sont que cette optimisation (hypothétique) ne serait pas rentable … en moyenne sur tout le code Java.

L’autre problème est que cette optimisation (hypothétique) peut créer des failles de sécurité.


La chose intéressante est que le JLS ne semble pas spécifier que le membre values() renvoie une copie de tableau. Le bon sens 1 dit qu’il faut faire … mais ce n’est pas vraiment spécifié.

1 – Ce serait une faille de sécurité béante si values() renvoyait un tableau partagé (mutable) de valeurs enum .