2009-07-22 17 views
59

W Javie można tworzyć enum następująco:W jaki sposób zaimplementowano wartości() dla Java 6?

public enum Letter { 
    A, B, C, D, E, F, G; 

    static { 
     for(Letter letter : values()) { 
      // do something with letter 
     } 
    } 
} 

To pytanie dotyczy „wartości()” metoda. W szczególności, w jaki sposób jest wdrażany? Zwykle mogłem przeskoczyć do źródła dla klas Java za pomocą F3 lub CTRL + Click Eclipse (nawet dla klas takich jak String, Character, Integer, a nawet Enum). Możliwe jest wyświetlenie źródła innych metod wyliczania (np. ValueOf (String)).

Czy "wartości()" tworzą nową tablicę za każdym razem, gdy się ją wywołuje? Jeśli przypiszę je do zmiennej lokalnej, a następnie zmodyfikuję jeden z elementów, co się stanie (oczywiście nie wpłynie to na wartość zwróconą przez wartości(), co oznacza, że ​​za każdym razem nowa tablica jest przydzielana).

Czy kod jest oryginalny? Lub czy JVM/kompilator traktuje je specjalnie, zwracając tylko nowe wystąpienie z wartości(), gdy nie może udowodnić, że nie zostanie zmodyfikowane.

Odpowiedz

96

Zasadniczo kompilator (javac) tłumaczy twoje wyliczenie na statyczną tablicę zawierającą wszystkie twoje wartości podczas kompilacji. Kiedy wywołujemy wartości(), dajemy kopię .clone'd() tej tablicy.

Biorąc pod uwagę to proste wyliczenia:

public enum Stuff { 
    COW, POTATO, MOUSE; 
} 

Można rzeczywiście spojrzeć na kod, który Java generuje:

public enum Stuff extends Enum<Stuff> { 
    /*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(String name) { 
     return (Stuff)Enum.valueOf(Stuff.class, name); 
    } 

    private Stuff(/*synthetic*/ String $enum$name, /*synthetic*/ int $enum$ordinal) { 
     super($enum$name, $enum$ordinal); 
    } 
} 

Możesz zajrzeć jak javac „przekłada” swoje zajęcia, dokonując katalogu tymczasowego i działa:

javac -d <output directory> -XD-printflat filename.java 
+1

Czy system.arraycopy nie byłby szybszy? – Gandalf

+0

+1; dodatkowe kudo, jeśli wyjaśnisz znaczenie słowa "syntetyczne". – wchargin

+3

Dlaczego 'wartości()' metoda 'clone()' tablica '$ VALUES'? Dlaczego nie po prostu zwrócić go bezpośrednio? – RestInPeace

2

Po przypisaniu zmiennej lokalnej jedyną rzeczą, którą można zmodyfikować, jest przypisanie innej wyliczenia do tej zmiennej. Nie zmieni to samego wyliczenia, ponieważ zmieniasz tylko obiekt, do którego odnoszą się zmienne odniesienia.

Wygląda na to, że wyliczenia są w rzeczywistości pojedynczymi znakami, więc tylko jeden element z każdego wyliczenia może istnieć w tobie cały program, dlatego operator == jest legalny do wyliczenia.

Nie ma więc problemu z wydajnością i nie można przypadkowo zmienić czegoś w definicji wyliczeniowej.

+2

Uważam, że OP oznacza modyfikowanie tablicy zwróconej przez wartości(). jeśli jest to ten sam obiekt tablicy, który jest przechowywany wewnętrznie (zamiast kopii), to modyfikowanie go (np. przypisywanie jednego elementu do drugiego, przypisywanie wartości zerowej do elementu itp.) zepsułoby to nie tylko dla klasy enum, ale dla jakichkolwiek przyszłych połączeń z wartościami() – newacct

+1

Tak, masz rację. Jeśli nie było klonu, możesz usunąć Enum z tablicy, a późniejsze wywołania z wartości pomijają tę wartość. – Janusz

+0

A jednak to tylko płytka kopia, ponieważ instancje wyliczeniowe są unikalne. –

1

Czy kod jest źródłowy? Lub czy JVM/kompilator traktuje je specjalnie, zwracając tylko nowe wystąpienie z wartości(), gdy nie może udowodnić, że nie zostanie zmodyfikowane.

1) Nie, a przynajmniej nie w obecnych wdrożeniach. Zobacz odpowiedź @lucasmo dla dowodów.

2) AFAIK, nie.

Hipotetycznie może to zrobić. Jednak udowodnienie, że tablica nie jest lokalnie modyfikowana, byłoby skomplikowane i stosunkowo kosztowne dla JIT. Jeśli tablica "ucieka" z metody o nazwie values(), staje się bardziej skomplikowana.

Istnieje szansa, że ​​ta (hipotetyczna) optymalizacja nie opłaciłaby się ... kiedy uśredniono cały kod Java.

Inną kwestią jest to, że ta (hipotetyczna) optymalizacja może powodować luki w zabezpieczeniach.


Interesującą rzeczą jest to, że JLS nie wydaje się, aby określić, że człon values() zwraca kopię tablicy. Zdrowy rozsądek mówi, że to musi do ... ale nie jest faktycznie określony.

1 - Byłaby to luka w zabezpieczeniach, gdyby values() zwrócił udostępnioną (zmienną) tablicę wartości enum.

Powiązane problemy