2012-01-31 12 views
8

Ho trovato questo piccolo gioiello nel nostro codebase al lavoro di recente. Devo confessare che non ho assolutamente idea del perché questo enum è stato scritto in questo modo (nomi cambiati per proteggere gli innocenti):Utilizzo di sun.misc.SharedSecrets

package foo.bar; 

import sun.misc.SharedSecrets; 
import foo.baz.HasAGetValuesMethod; 

public enum MysteryEnum implements HasAGetValuesMethod { 

    THINGY, BOB; 

    @Override 
    public MysteryEnum[] getValues() { 
     return SharedSecrets.getJavaLangAccess().getEnumConstantsShared(MysteryEnum .class); 
    } 
} 

Negli getValues ​​() il metodo invece di limitarsi a chiamare MysteryEnum.values() sta usando qualcosa chiamato sun.misc.SharedSecret per ottenere un handle per qualcosa chiamato sun.misc.JavaLangAccess, quindi utilizzando che per ottenere una matrice di tutti i valori enum. Il Javadoc in quella classe ti dice quale sia il metodo, ma non riesco a trovare molto su perché lo vuoi chiamare.

Lo sviluppatore che ha scritto questo non è più in giro, quindi non lo può chiedere. Chiederò comunque alla mia squadra, ma ho la sensazione che la risposta sarà: "Non so perché lo fa, ma meglio non cambiarlo". Per il momento, presumo che questo sia un caso strano per qualcuno che non sa che il metodo values() esiste, o che la mia ignoranza delle librerie di sun.misc mi sta facendo mancare qualcosa di ovvio agli altri. Qualche idea spiega perché questo codice sia stato scritto in questo modo?

risposta

7

Il metodo restituisce lo stesso array senza riflessione o copia/clonazione matrice sottostante. Ciò migliora le prestazioni, ma non è una buona idea espone un array mutabile.

for (int i = 0; i < 3; i++) 
    System.out.println(SharedSecrets.getJavaLangAccess().getEnumConstantsShared(AccessMode.class)); 
AccessMode[] ams = SharedSecrets.getJavaLangAccess().getEnumConstantsShared(AccessMode.class); 
ams[1] = ams[2]; // don't do this !! 
System.out.println(EnumSet.allOf(AccessMode.class)); 

stampe

[Ljava.nio.file.AccessMode;@330cdec1 
[Ljava.nio.file.AccessMode;@330cdec1 
[Ljava.nio.file.AccessMode;@330cdec1 
[READ, EXECUTE, EXECUTE] 

invece di utilizzare questo metodo, quello che faccio è usare la mia copia memorizzata nella cache

// cannot be modified. 
private static final AccessMode[] ACCESS_MODES = AccessMode.values(); 
+0

Capisco. Quindi è un hack delle prestazioni. A giudicare dall'aspetto, piuttosto fragile. – Jon

+0

È abbastanza oscuro, sotto 'sole. * ', Che dovresti sapere per evitarlo o gestirlo con cura. Simile a 'sun.misc.Unsafe';) –

+0

Dopo una caccia, sembra che il metodo values ​​() sia effettivamente aggiunto dal compilatore (non è su Javadoc per Enum). Se questo è il caso, allora come possiamo essere sicuri che quanto sopra sia anche ottimizzato in qualche modo? Forse il compilatore è ottimizzato per restituire un handle allo stesso array dove vengono chiamati i valori? C'è un modo per controllarlo? – Jon

3

Fondamentalmente SharedSecret:

Un repository di "segreti condivisi", che sono un meccanismo per chiamare i metodi di implementazione e privato a un altro pacchetto senza utilizzando la riflessione.

il codice restituisce le costanti enum leggendo la classe e tornare indietro le costanti (senza bisogno di fare chiamate di riflessione). Questo è dinamico in modo che se una nuova costante enum viene aggiunta all'enum, il metodo getValues() restituirà le enumerazioni aggiunte (non è necessario cambiare il codice nell'intera esposizione).

+0

io non capisco. Se aggiungi ulteriori valori enum, quindi chiama valori() ottieni anche le enumerazioni aggiunte a quelle originali. Cosa offre getValues ​​(), come implementato, al di sopra di questo? – Jon

+0

Restituisce solo i valori originali esatti (senza necessità di clonazione o memorizzazione nella cache). Fondamentalmente, è un modo per aggirare lo stack JVM e ottenere il valore diretto. –

1

Il documentation dice:

Restituisce gli elementi di una classe enum o null se l'oggetto Class non non rappresenta un tipo enum; il risultato è non clonato, memorizzato nella cache e condiviso da tutti i chiamanti.

Quindi, a meno che il punto era quello di fornire una matrice comune, in modo che chiunque potesse rompere tutto impostando uno dei suoi elementi per nulla, o l'ordinamento di esso, o qualsiasi altra cosa (che avrebbe potuto essere fatto mettendo in cache il risultato di il metodo values()), la mia ipotesi è anche che questa linea è lì a causa dell'incompetenza dello sviluppatore precedente.

avrei scritto un test di unità e sostituirlo con una chiamata a values() e verificare che il test unità passa ancora.