Java garantisce di incorporare costanti di stringa se possono essere determinate al momento della compilazione
Domanda
Considera questo caso:
public Class1 {
public static final String ONE = "ABC";
public static final String TWO = "DEF";
}
public Class2 {
public void someMethod() {
System.out.println(Class1.ONE + Class1.TWO);
}
}
In genere ci si aspetta che il compilatore includa le costanti ONE e TWO. Tuttavia, questo comportamento è garantito? È possibile distribuire in runtime Class2 senza Class1 nel percorso di classe e aspettarsi che funzioni indipendentemente dai compilatori o si tratta di un'ottimizzazione del compilatore opzionale?
EDIT: Perché mai farlo? Bene, ho una costante che sarebbe condivisa tra le due estremità di un'applicazione (client e server su RMI) e sarebbe molto conveniente in questo caso particolare mettere la costante su una classe che può essere solo su un lato di quella divisione ( poiché è logicamente quello che possiede quel valore costante) piuttosto che averlo in una classe di costanti arbitrarie solo perché deve essere condiviso da entrambi i lati del codice. Al momento della compilazione è tutto un set di file di origine, ma al momento della compilazione è diviso per pacchetto.
Soluzione
È garantito che sia trattato come un'espressione costante e garantito da internato da sezione 15.28 del JLS :
Un'espressione costante durante la compilazione è un'espressione che indica un valore di tipo primitivo o una stringa che lo fa non si completa bruscamente ed è composto utilizzando solo quanto segue:
- Letterali di tipo primitivo e letterali di tipo String (& # 167; 3.10.5)
- Lancia a tipi primitivi e lancia per digitare String
- Gli operatori unari +, -, ~ e! (ma non ++ o -)
- Gli operatori moltiplicativi *, / e%
- Gli operatori additivi + e -
- ...
...
Costanti in fase di compilazione di tipo String sono sempre "internati" in modo da condividere istanze uniche, usando il metodo String.Intern.
Ora, ciò non significa che sia garantito che sia inline. Tuttavia, la sezione 13.1 delle specifiche dice:
Riferimenti a campi che sono costanti le variabili (& # 167; 4.12.4) vengono risolte in compilare il tempo al valore costante questo è indicato. Nessun riferimento a tale un campo costante dovrebbe essere presente in il codice in un file binario (tranne in la classe o l'interfaccia contenente il file campo costante, che avrà codice per inizializzarlo) e tale costante i campi devono sempre essere stati inizializzato; il valore iniziale predefinito per il tipo di tale campo deve non essere mai osservato.
In altre parole, anche se l'espressione stessa non era una costante , non dovrebbe esserci alcun riferimento a Class1
. Quindi sì, stai bene. Ciò non necessariamente garantisce che il valore concatenato sia utilizzato nel bytecode, ma i bit a cui fa riferimento in precedenza garantiscono che il valore concatenato viene internato, quindi sarei enormemente sorpreso se non solo incorporasse il valore concatenato. Anche se non funziona, sei sicuro che funzionerà senza Class1
.
Altri suggerimenti
Compilando che con javac 1.6.0_14 si ottiene il seguente bytecode:
public void someMethod();
Code:
0: getstatic #2; //Field java/lang/System.out:Ljava/io/PrintStream;
3: ldc #3; //String ABCDEF
5: invokevirtual #4; //Method java/io/PrintStream.println:(Ljava/lang/String;)V
8: return
Quindi le stringhe vengono concatenate al momento della compilazione e il risultato è incluso nel pool costante di Class2.
Non sarà integrato dal compilatore ma dall'interprete in fase di esecuzione e, se possibile, convertito in codice assembly.
Non può essere garantito, perché non tutti gli interpreti (JVM) funzionano allo stesso modo. Ma le implementazioni più importanti lo faranno.
Purtroppo non ho un link per sostenere questo :(
Sospetto, ma non so per certo, che funzionerà, ma non sembra una buona idea.
Il "normale" modi per farlo sono:
- Metti le costanti in un pacchetto condiviso tra il client e il server. Presumibilmente, esiste un pacchetto del genere, perché è lì che vanno le interfacce.
- Se non esiste un pacchetto simile, creare 2 classi con le costanti condivise: una per il server e una per il client.
Vedi JLS 13.4.9 . Sebbene non richieda esplicitamente che le costanti siano incorporate dal compilatore, suggerisce che la compilazione condizionale e il supporto per le costanti nelle istruzioni switch
inducono sempre il compilatore a incorporare costanti.
Sembra che tu stia codificando la tua versione della funzionalità integrata in enum
, che public static final
per te, denominazione corretta tramite name ( )
e toString ()
(oltre ad avere alcuni altri vantaggi, ma forse avere lo svantaggio di un footprint di memoria maggiore).
Stai usando una versione precedente di Java che non include ancora enum?