2016-04-19 30 views
14

So che un enumCosa rende enum in java non istantanea?

enum Year 
{ 
    First, Second, Third, Fourth; 
} 

viene convertito in

final class Year extends Enum<Year> 
{ 
     public static final Year First = new Year(); 
     public static final Year Second = new Year(); 
     public static final Year Third = new Year(); 
     public static final Year Fourth = new Year(); 
} 

Quando ho cercato di creare un'istanza di enum (non classe) ho ottenuto compilazione errore di tempo come:

error: enum types may not be instantiated 
     Year y = new Year(); 

Come da la mia conoscenza di un costruttore privato rende una classe non istantanea. E ho pensato che il compilatore stia fornendo un costruttore privato. Ma di nuovo mi sono confuso quando ho visto che possiamo definire un costruttore per enum con modificatore di default e ancora non è possibile creare un oggetto di tipo enum.

enum Year 
{ 
     First, Second, Third, Fourth; 
     Year() 
     { 
     } 
} 

class Example 
{ 
     public static void main(String[] args) 
     { 
       Year y = new Year(); 
     } 
} 

mio dubbio è, se non si tratta di costruttori allora che cosa rende enum in Java non istanziabile?

+8

"Perché la lingua lo dice." Davvero, non c'è risposta migliore di quella. La tua dichiarazione 'enum' fa in modo che quattro oggetti siano _pre-created_, e ogni oggetto della classe deve essere uno di questi quattro. Quindi non c'è mai un motivo per istanziarne un altro. – ajb

+1

Nota a margine: un 'enum Year' è compilato in un' classe finale Anno Enum '. – Seelenvirtuose

risposta

19

è specificato nel Java Language Specification:

8.9. Enum Types

...

un tipo enum non ha casi diversi da quelli definiti dalle sue costanti enum. È un errore in fase di compilazione per tentare di istanziare esplicitamente un tipo di enum (§15.9.1).

Quindi il compilatore assicura che questo requisito sia soddisfatto. Poiché il compilatore "sa" che il tipo è un enum, è in grado di distinguere tra enum Year e final class Year.

Inoltre, nessun modificatore di accesso è consentito per un costruttore enum:

8.9.2. Enum Body Declarations

...

Si tratta di un errore di compilazione se una dichiarazione del costruttore in una dichiarazione enum è pubblico o protetto.

...

In una dichiarazione enum, una dichiarazione costruttore senza modificatori di accesso è privata.

Quindi, in pratica, un costruttore enum ha l'aspetto del pacchetto (nessun modificatore di accesso), ma è davvero privato.

Infine, la stessa sezione stabilisce inoltre

In una dichiarazione enum senza dichiarazioni costruttore, un costruttore di default viene implicitamente dichiarato. Il costruttore predefinito è privato, non ha parametri formali e non ha clausole di tiri.

Questo rende il enum non-istante anche se nessun costruttore è dichiarato esplicitamente.

+3

La tua risposta dimentica anche un punto importante: non è solo una cosa in fase di compilazione: il compilatore genera effettivamente un costruttore privato per l'enumerazione, rendendo impossibile l'istanziazione. Facilmente reperibile usando 'javap -c -p'. –

+4

@JB Nizet: ma non sovrascrivere il modificatore 'private'. Per le classi ordinarie, un costruttore 'private' consente di creare nuove istanze all'interno della stessa classe o anche all'interno di classi nidificate, ma per i tipi' enum', il compilatore vieta anche questo. Inoltre, l'istanza di Reflection 'Constructor' esegue un controllo aggiuntivo per i tentativi di istanziazione' enum'.Ma se ti attacchi abbastanza profondamente a Reflection troverai dei modi per aggirare quel controllo (e quindi, un modificatore 'privato' non è un ostacolo) ... – Holger

5

Un enum in Java ha un costruttore predefinito quando non è definito ed è privato.

Il modificatore di accesso predefinito ha significati diversi in ambiti diversi. Ad esempio all'interno di un modificatore di accesso predefinito di classe per metodi e campi è package private.

Dove come in un modificatore di accesso predefinito di interfaccia significa public. In realtà non ci può essere nessun altro modificatore su un campo di interfaccia quindi è implicitamente pubblico.

Su una classe di livello superiore E 'pacchetto privata (dove solo 2 modificatori di accesso sono consentiti public e pacchetto predefinito privato)

Quindi la risposta alla tua domanda è che è così perché il compilatore decide così. Lo scrittore di compilatori ha dovuto sostenere il contratto di specifica della lingua.

Hai ragione nel pensare che è una classe normale dopo tutto. Ogni tipo di blueprint di un oggetto in java è una classe che può essere rappresentata da java.lang.Class. Queste restrizioni per interfacce, enumerazioni, classi astratte, classi anonime, classi locali dei metodi sono validate solo dal compilatore.

Se è possibile in qualche modo sfuggire al compilatore e generare il proprio codice byte per enumerazione o altro modo se è possibile modificare il codice byte della classe generata enum in modo che il costruttore privato diventi pubblico potrebbe essere possibile chiamarlo costruttore al di fuori dell'ambito privato dell'enum. Puoi anche provare a sperimentare con la riflessione per fare lo stesso. Infatti generando codice byte manualmente i linguaggi JVM come Groovy, Jython, JRuby, Clojure sono in grado di fornire funzionalità che non sono in Java stesso. Stanno bypassando il compilatore java.

Scopo per il costruttore in enum s è essere in grado di impostare campi di costanti in una chiamata. Tutte le costanti all'interno di enum sono istanze della classe enum, quindi sono anche i campi dichiarati in essa.

enum Test 
{ 
    T1(1), // equivalent to public static final Test T1 = new Test(1); 
    T2(2); // equivalent to public static final Test T2 = new Test(2); 

    int id; 
    Test(int id) 
    { 
     this.id = id; 
    } 
} 

Infine soffietto è l'uscita di codice decompilato per sopra enum utilizzando java -p Test.class

final class Test extends java.lang.Enum<Test> 
{ 
    public static final Test T1; 
    public static final Test T2; 
    int id; 
    private static final Test[] $VALUES; 
    public static Test[] values(); 
    public static Test valueOf(java.lang.String); 
    private Test(int); 
    static {}; 
} 

Si dovrebbe dare una migliore comprensione di ciò che accade quando la classe compila.