Può cambiare un valore di una costante nel tempo?

28

Durante la fase di sviluppo, ci sono alcune variabili che devono essere fissate nella stessa esecuzione, ma potrebbe aver bisogno di essere modificate nel tempo. Ad esempio un boolean per segnalare la modalità di debug, quindi facciamo cose nel programma che normalmente non faremmo.

È uno stile sbagliato contenere questi valori in una costante, ovvero final static int CONSTANT = 0 in Java? So che una costante rimane la stessa durante il tempo di esecuzione, ma dovrebbe anche essere la stessa durante l'intero sviluppo, tranne che per le modifiche non pianificate, ovviamente?

Ho cercato domande simili, ma non ho trovato nulla che corrispondesse esattamente al mio.

    
posta GregT 08.01.2018 - 14:52
fonte

7 risposte

6

In Java, le costanti finali statiche possono essere copiate dal compilatore come valori, nel codice che li usa . Di conseguenza, se si rilascia una nuova versione del codice e c'è una dipendenza downstream che ha utilizzato la costante, la costante in tale codice non verrà aggiornata a meno che il codice downstream non venga ricompilato. Questo può essere un problema se poi fanno uso di quella costante con il codice che si aspetta il nuovo valore, poiché anche se il codice sorgente è corretto, il codice binario non lo è.

Questa è una verruca nella progettazione di Java, dal momento che è uno dei pochissimi casi (forse l'unico caso) in cui compatibilità di origine e compatibilità binaria non sono gli stessi. Tranne che per questo caso, è possibile scambiare una dipendenza con una nuova versione compatibile con API senza che gli utenti della dipendenza debbano ricompilare. Ovviamente questo è estremamente importante dato il modo in cui le dipendenze Java sono generalmente gestite.

A peggiorare le cose è che il codice farà semplicemente la cosa sbagliata piuttosto che produrre errori utili. Se dovessi sostituire una dipendenza con una versione con definizioni di classi o metodi incompatibili, avresti degli errori di classloader o di chiamata, che forniscono almeno buoni indizi su quale sia il problema. A meno che tu non abbia cambiato il tipo di valore, questo problema apparirà solo come comportamento anomalo runtime misterioso.

Più fastidioso è che le JVM di oggi potrebbero facilmente integrare tutte le costanti in fase di esecuzione senza penalizzazione delle prestazioni (oltre alla necessità di caricare la classe che definisce la costante, che probabilmente viene comunque caricata), sfortunatamente la semantica della lingua data dal giorni prima delle JIT. E non possono cambiare la lingua perché il codice compilato con i compilatori precedenti non sarà corretto. La compatibilità con Bugward colpisce di nuovo.

A causa di tutto questo, alcune persone consigliano di non modificare affatto un valore finale statico. Per le librerie che potrebbero essere ampiamente distribuite e aggiornate in modi sconosciuti in tempi sconosciuti, questa è una buona pratica.

Nel tuo codice, specialmente nella parte superiore della gerarchia delle dipendenze, probabilmente lo farai franca. Ma in questi casi, considera se hai davvero bisogno che la costante sia pubblica (o protetta). Se la costante è solo visibilità del pacchetto, è ragionevole, in base alle circostanze e agli standard del codice, che l'intero pacchetto verrà sempre ricompilato in una sola volta e il problema scompare. Se la costante è privata, non hai problemi e puoi cambiarla quando vuoi.

    
risposta data 08.01.2018 - 22:17
fonte
85

Qualunque cosa nel tuo codice sorgente, incluse% costanti globali dichiarate come% co_de, potrebbe essere soggetta a modifiche con una nuova versione del tuo software.

Le parole chiave const (o const in Java) sono lì per segnalare al compilatore che questa variabile non cambierà mentre questa istanza del programma è in esecuzione . Niente di più. Se vuoi inviare messaggi al prossimo manutentore, usa un commento in codice, ecco a cosa servono.

// DO NOT CHANGE without consulting with the legal department!
// Get written consent form from them before release!
public const int LegalLimitInSeconds = ...

È un modo migliore per comunicare con il tuo sé futuro.

    
risposta data 08.01.2018 - 15:05
fonte
13

Dobbiamo distinguere due aspetti delle costanti:

  • nomi per i valori noti al momento dello sviluppo, che introduciamo per una migliore manutenibilità e
  • valori disponibili per il compilatore.

E poi c'è un terzo tipo correlato: variabili il cui valore non cambia, cioè i nomi di un valore. La differenza tra queste variabili immutabili e una costante è quando il valore è determinato / assegnato / inizializzato: una variabile viene inizializzata in fase di esecuzione, ma il valore di una costante è noto durante lo sviluppo. Questa distinzione è un po 'confusa dal momento che un valore può essere conosciuto durante lo sviluppo, ma in realtà viene creato solo durante l'inizializzazione.

Ma se il valore di una costante è noto in fase di compilazione, allora il compilatore può eseguire calcoli con quel valore. Ad esempio, il linguaggio Java ha il concetto di espressioni costanti . Un'espressione costante è qualsiasi espressione che consiste solo di valori letterali di primitive o stringhe, operazioni su espressioni costanti (come casting, addizione, concatenazione di stringhe) e di variabili costanti. [ JLS §15.28 ] Una variabile costante è un final variabile che è inizializzata con un'espressione costante. [JLS §4.12.4] Quindi per Java, questa è una costante in fase di compilazione:

public static final int X = 7;

Questo diventa interessante quando una variabile costante viene utilizzata in più unità di compilazione e quindi la dichiarazione viene modificata. Prendere in considerazione:

  • A.java :

    public class A { public static final int X = 7; }
    
  • B.java :

    public class B { public static final int Y = A.X + 2; }
    

Ora quando compiliamo questi file il B.class bytecode dichiarerà un campo Y = 9 perché B.Y è una variabile costante.

Ma quando cambiamo la variabile A.X in un valore diverso (ad esempio, X = 0 ) e ricompiliamo solo il file A.java , allora B.Y si riferisce ancora al vecchio valore. Questo stato A.X = 0, B.Y = 9 non è coerente con le dichiarazioni nel codice sorgente. Buon debugging!

Questo non significa che le costanti non dovrebbero mai essere cambiate. Le costanti sono definitivamente migliori dei numeri magici che appaiono senza spiegazione nel codice sorgente. Tuttavia, il valore delle costanti pubbliche è parte della tua API pubblica . Questo non è specifico di Java, ma si verifica anche in C ++ e in altri linguaggi che dispongono di unità di compilazione separate. Se cambi questi valori, dovrai ricompilare tutto il codice dipendente, cioè eseguire una compilazione pulita.

A seconda della natura delle costanti, potrebbero aver portato a supposizioni errate da parte degli sviluppatori. Se questi valori vengono modificati, potrebbero causare un errore. Ad esempio, un insieme di costanti può essere scelto in modo che formino determinati schemi di bit, ad es. %codice%. Se questi vengono modificati per formare una struttura diversa come public static final int R = 4, W = 2, X = 1 , il codice esistente come R = 0, W = 1, X = 2 diventa errato. E pensa al divertimento che ne deriverebbe se boolean canRead = perms & R cambiasse! Non c'è alcuna correzione qui, è solo importante ricordare che il valore delle costanti alcune è davvero importante e non può essere modificato semplicemente.

Ma per la maggior parte delle costanti che li cambiano sta andando bene finché si considerano le restrizioni di cui sopra. Una costante è sicura da cambiare quando il significato, non il valore specifico è importante. Questo è ad es. il caso per i sintonizzabili come Integer.MAX_VALUE o BORDER_WIDTH = 2 o modelli come TIMEOUT = 60; // seconds - sebbene probabilmente alcuni o tutti quelli dovrebbero essere specificati nei file di configurazione piuttosto che nel codice.

    
risposta data 08.01.2018 - 16:27
fonte
6

Una costante è garantita come costante per la durata del runtime dell'applicazione . Finché ciò è vero, non vi è alcun motivo per non sfruttare la funzionalità del linguaggio. Hai solo bisogno di sapere quali sono le conseguenze per l'utilizzo di un flag costante rispetto al compilatore per lo stesso scopo:

  • Le costanti occupano spazio applicativo
  • Flag del compilatore non
  • Il codice disattivato dalle costanti può essere aggiornato e modificato con i moderni strumenti di refactoring
  • Il codice disattivato dai flag del compilatore non può

Avendo mantenuto un'applicazione che avrebbe attivato il disegno di riquadri di delimitazione per le forme in modo da poter eseguire il debug del modo in cui sono stati disegnati, abbiamo riscontrato un problema. Dopo il refactoring, tutto il codice disattivato dai flag del compilatore non veniva compilato. Dopodiché, abbiamo intenzionalmente modificato i flag del compilatore con le costanti dell'applicazione.

Lo sto dicendo per dimostrare che ci sono dei compromessi. Il peso di pochi booleani non avrebbe reso l'applicazione a corto di memoria o occupato troppo spazio. Questo potrebbe non essere vero se la tua costante è in realtà un oggetto di grandi dimensioni che ha essenzialmente un handle per tutto ciò che nel tuo codice. Se non si cura di rimuovere tutti i riferimenti che contiene su un oggetto dopo che non è più necessario, allora l'oggetto potrebbe essere la fonte di una perdita di memoria.

You need to evaluate the use case and why you would want to change the constants.

Non sono un fan delle semplici dichiarazioni generali, ma in generale il tuo collega senior è corretto. Se qualcosa è destinato a cambiare spesso, potrebbe essere necessario un elemento configurabile. Ad esempio, potresti probabilmente convincere il tuo collega per una percentuale costante diIsInDebugMode = true quando vuoi proteggere del codice da eventuali errori. Tuttavia, alcune cose potrebbero dover essere modificate più spesso di quanto non rilasci un'applicazione. In tal caso, è necessario un modo per modificare tale valore al momento opportuno. Puoi prendere l'esempio di TaxRate = .065 . Questo potrebbe essere vero al momento della compilazione del codice, ma a causa di nuove leggi può cambiare prima di rilasciare la versione successiva dell'applicazione. Questo è qualcosa che deve essere aggiornato da qualche meccanismo di archiviazione (come file o database)

    
risposta data 08.01.2018 - 15:18
fonte
2

Il const , #define o final è un suggerimento del compilatore (si noti che #define non è in realtà un suggerimento, è una macro e significativamente più potente). Indica che il valore non cambierà rispetto all'esecuzione di un programma e potrebbero essere eseguite varie ottimizzazioni.

Tuttavia, come suggerimento del compilatore, il compilatore fa cose che potrebbero non essere sempre previste dal programmatore. In particolare, javac inline un static final int FOO = 42; in modo che ovunque venga utilizzato FOO , il codice byte compilato effettivo leggerà 42 .

Questa non è una sorpresa troppo grande finché qualcuno non cambia il valore senza ricompilare l'altra unità di compilazione (file .java) - e 42 rimane nel codice byte (vedi è possibile disabilitare l'inlining di javac delle variabili finali statiche? ).

Rendere qualcosa static final significa che è quello e per sempre lo sarà e cambiarlo è un vero affare - specialmente se è tutto tranne private .

Le costanti per cose come final static int ZERO = 0 non sono un problema. final static double TAX_RATE = 0.55 (a parte il fatto che il denaro e il doppio sono cattivi e dovrebbe usare BigDecimal, ma non è un primitivo e quindi non è in linea) è un problema e dovrebbe essere esaminato con attenzione dove viene utilizzato.

    
risposta data 08.01.2018 - 16:22
fonte
1

Come suggerisce il nome, le costanti non dovrebbero cambiare durante il runtime e secondo me le costanti sono definite per non cambiare a lungo termine (si può guardare questa domanda SO per ulteriori informazioni.

Quando si tratta di necessità di flag (ad esempio per la modalità di sviluppo) si dovrebbe invece usare un file di configurazione o un parametro di avvio (molti IDE supportano la configurazione del parametro di avvio su una base per progetto, fare riferimento alla documentazione pertinente) per abilitare questa modalità - in questo modo si mantiene la flessibilità di utilizzare tale modalità e non si può dimenticare di cambiarla ogni volta che il codice diventa produttivo.

    
risposta data 08.01.2018 - 15:05
fonte
0

Essere in grado di essere cambiato tra una corsa e l'altra è uno dei punti più importanti per definire una costante nel codice sorgente!

La costante offre una posizione ben definita e documentata (in un certo senso) per modificare il valore ogni volta che è necessario durante la vita del codice sorgente. È anche una promessa che cambiare la costante in questa posizione cambierà effettivamente tutte le occorrenze di ciò che rappresenta.

Come esempio negativo: non ha senso avere una costante TRUE che passa a true in una lingua che ha effettivamente la parola chiave true . Mai e poi mai neanche una volta, dichiarerai TRUE=false tranne che come uno scherzo crudele.

Naturalmente ci sono altri usi delle costanti, ad esempio il codice di accorciamento ( CO_NAME = 'My Great World Unique ACME Company' ), evitando la duplicazione ( PI=3.141 ), l'impostazione delle convenzioni ( TRUE=1 ) o qualsiasi altra cosa, ma avendo una posizione definita per cambiare la costante è sicuramente una delle più importanti.

    
risposta data 09.01.2018 - 14:22
fonte

Leggi altre domande sui tag