Un valore di una costante può essere modificato nel tempo?


28

Durante la fase di sviluppo, ci sono alcune variabili che devono essere fisse nella stessa corsa, ma potrebbero dover essere modificate nel tempo. Ad esempio, booleanper segnalare la modalità di debug, quindi facciamo cose nel programma che normalmente non faremmo.

È cattivo stile contenere questi valori in una costante, cioè final static int CONSTANT = 0in Java? So che una costante rimane la stessa durante il runtime, ma dovrebbe anche essere la stessa durante l'intero sviluppo, ad eccezione delle modifiche non pianificate, ovviamente?

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


11
Sono curioso, perché dovresti credere che sia un cattivo stile cambiare questi?
Vincent Savard

36
A meno che non modelliate le proprietà fisiche con costanti che hanno valori matematici noti, tutto può cambiare in un determinato momento.
Berin Loritsch,

19
il software è morbido .
Erik Eidt,

10
@GregT Non sarei d'accordo. finalti dà una garanzia imposta dal compilatore che il programma non modificherà il valore. Non vorrei rinunciare a questo solo perché il programmatore potrebbe voler modificare il valore assegnato nel codice sorgente.
Alexander - Ripristina Monica l'

10
Non c'è molto tempo per articolare una risposta completa, ma sospetto che i tuoi colleghi non si preoccupino tanto delle costanti, ma dell'integrazione del codice dei valori di configurazione, che potrebbero tendere a manifestarsi come costanti. ... le costanti ti proteggono da errori stupidi, come l'assegnazione accidentale a gravitymetà partita / corsa. Non significano necessariamente, gravityè lo stesso su ogni pianeta ... Detto questo, la soluzione salutare è quella di creare gravityuna costante, ma di estrarla da un planetfile o database all'inizio del relativo ambito.
svidgen,

Risposte:


6

In Java, le costanti finali statiche possono essere copiate dal compilatore, come i loro valori, nel codice che le utilizza . Di conseguenza, se si rilascia una nuova versione del codice e si verifica una dipendenza downstream che ha utilizzato la costante, la costante in quel codice non verrà aggiornata a meno che non venga ricompilato il codice downstream. Questo può essere un problema se poi usano quella costante con il codice che prevede il nuovo valore, come anche se il codice sorgente è giusto, il codice binario non lo è.

Questa è una verruca nella progettazione di Java, poiché è uno dei pochissimi casi (forse l'unico caso) in cui la compatibilità dei sorgenti e la compatibilità binaria non sono le stesse. Tranne 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à silenziosamente la cosa sbagliata anziché produrre errori utili. Se si dovesse sostituire una dipendenza con una versione con definizioni di classe o metodo incompatibili, si otterrebbero errori di caricamento della classe o di invocazione, che almeno forniscono buoni indizi su quale sia il problema. A meno che tu non abbia cambiato il tipo di valore, questo problema apparirà come un misterioso malfunzionamento in fase di esecuzione.

Più fastidioso è il fatto che le JVM di oggi potrebbero facilmente incorporare tutte le costanti in fase di esecuzione senza penalità di prestazione (oltre alla necessità di caricare la classe che definisce la costante, che probabilmente viene caricata comunque), sfortunatamente la semantica della data della lingua dai giorni precedenti le JIT . E non possono cambiare la lingua perché il codice compilato con i compilatori precedenti non sarà corretto. La compatibilità con bugward colpisce ancora.

Per questo motivo alcune persone consigliano di non modificare affatto un valore finale statico. Per le biblioteche che potrebbero essere ampiamente distribuite e aggiornate in modi sconosciuti in momenti sconosciuti, questa è una buona pratica.

Nel tuo codice, specialmente nella parte superiore della gerarchia delle dipendenze, probabilmente te ne andrai via. Ma in questi casi, considera se hai davvero bisogno che la costante sia pubblica (o protetta). Se la costante è solo visibilità del pacchetto, è ragionevole, a seconda delle circostanze e degli standard del codice, che l'intero pacchetto venga sempre ricompilato immediatamente e il problema scompaia. Se la costante è privata, non hai problemi e puoi cambiarla quando vuoi.


85

Qualunque cosa nel tuo codice sorgente, comprese le constcostanti globali dichiarate, potrebbe essere soggetta a modifiche con una nuova versione del tuo software.

Le parole chiave const(o finalin 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 nella fonte, 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.


11
Mi è piaciuto molto questo commento per il futuro io.
GregT

4
TaxRateessere publicmi rende nervoso. Vorrei sapere con certezza che solo il reparto vendite è interessato da questo cambiamento e non anche i nostri fornitori che ci applicano una tassa. Chissà cosa è successo nella base di codice da quando è stato scritto quel commento.
candied_orange

3
@IllusiveBrian non stava criticando l'uso delle costanti. Stavo avvisando di non fidarmi di un commento per essere aggiornato. Assicurati sempre di come viene usato qualcosa prima di cambiarlo.
candied_orange

8
Questo è un buon consiglio per Java . Potrebbe essere diverso in altre lingue. Ad esempio, a causa del modo in cui i valori const sono associati al sito di chiamata in C #, i public constcampi devono essere utilizzati solo per cose che non cambieranno mai, come Math.pi. Se stai creando una libreria, le cose che potrebbero cambiare durante lo sviluppo o con una nuova versione dovrebbero essere public static readonly, in modo da non causare problemi con gli utenti della tua libreria.
GrandOpener

6
Dovresti scegliere un esempio diverso ... i valori in denaro non dovrebbero mai essere in virgola mobile!
corsiKa

13

Dobbiamo distinguere due aspetti delle costanti:

  • nomi per valori noti in fase di 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, ovvero i nomi per un valore. La differenza tra queste variabili immutabili e una costante è quando il valore viene 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 poiché 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 al momento della compilazione, 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 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 è una finalvariabile inizializzata con un'espressione costante. [JLS §4.12.4] Quindi per Java, questa è una costante di compilazione:

public static final int X = 7;

Ciò diventa interessante quando una variabile costante viene utilizzata in più unità di compilazione e quindi la dichiarazione viene modificata. Ritenere:

  • 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.classbytecode dichiarerà un campo Y = 9perché B.Yè una variabile costante.

Ma quando cambiamo la A.Xvariabile in un valore diverso (diciamo, X = 0) e ricompiliamo solo il A.javafile, allora facciamo B.Yancora riferimento al vecchio valore. Questo stato non A.X = 0, B.Y = 9è coerente con le dichiarazioni nel codice sorgente. Buon debug!

Ciò non significa che le costanti non debbano mai essere modificate. Le costanti sono decisamente migliori dei numeri magici che compaiono senza spiegazioni nel codice sorgente. Tuttavia, il valore delle costanti pubbliche fa parte dell'API pubblica . Questo non è specifico di Java, ma si verifica anche in C ++ e in altri linguaggi con unità di compilazione separate. Se si modificano questi valori, sarà necessario ricompilare tutto il codice dipendente, ovvero eseguire una compilazione pulita.

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

Ma per la maggior parte delle costanti cambiarle andrà bene fintanto che saranno considerate le restrizioni di cui sopra. Una costante è sicura da cambiare quando il significato, non il valore specifico è importante. Questo è ad esempio il caso di parametri sintonizzabili come BORDER_WIDTH = 2o TIMEOUT = 60; // secondso modelli come API_ENDPOINT = "https://api.example.com/v2/"- anche se probabilmente alcuni o tutti dovrebbero essere specificati nei file di configurazione piuttosto che nel codice.


5
Mi piace questa analisi. L'ho letto come: sei libero di cambiare una costante purché tu capisca come viene usato.
candied_orange

Anche +1 C # "soffre" dello stesso problema con le costanti pubbliche.
Reginald Blue

6

Una costante è garantita solo per la durata del runtime dell'applicazione . Finché ciò è vero, non c'è motivo di non sfruttare la funzionalità della lingua. Hai solo bisogno di sapere quali sono le conseguenze per l'uso di un flag costante vs. compilatore per lo stesso scopo:

  • Le costanti occupano spazio nell'applicazione
  • I flag del compilatore no
  • 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 gestito 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 venivano disegnati, abbiamo riscontrato un problema. Dopo il refactoring, tutto il codice che era stato disattivato dai flag del compilatore non veniva compilato. Successivamente, abbiamo intenzionalmente modificato i flag del compilatore in costanti dell'applicazione.

Lo dico per dimostrare che ci sono compromessi. Il peso di alcuni booleani non avrebbe fatto esaurire la memoria dell'applicazione o occupare troppo spazio. Questo potrebbe non essere vero se la tua costante è davvero un oggetto di grandi dimensioni che ha essenzialmente un handle per tutto nel tuo codice. Se non si cura di rimuovere tutti i riferimenti che contiene a un oggetto dopo che non è più necessario, il tuo oggetto potrebbe essere la fonte di una perdita di memoria.

È necessario valutare il caso d'uso e il motivo per cui si desidera modificare le costanti.

Non sono un fan delle semplici affermazioni generali , ma in generale il tuo collega senior ha ragione. Se qualcosa è destinato a cambiare spesso, potrebbe essere necessario essere un elemento configurabile. Ad esempio, potresti probabilmente convincere il tuo collega per una costante IsInDebugMode = truequando vuoi proteggere un po 'di codice dalla rottura. Tuttavia, alcune cose potrebbero dover cambiare più spesso di quanto rilasci un'applicazione. In tal caso, è necessario un modo per modificare tale valore al momento opportuno. Puoi prendere l'esempio di a TaxRate = .065. Ciò può 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)


Cosa intendi con "flag del compilatore"? Forse il preprocessore C e caratteristiche del compilatore simili che supportano le macro / definisce e #ifdefs? Poiché si basano sulla sostituzione testuale del codice sorgente, non fanno parte della semantica del linguaggio di programmazione. Nota che Java non ha un preprocessore.
amon

@amon, Java potrebbe non esserlo, ma lo fanno diverse lingue. Intendo #ifdefbandiere. Sebbene non facciano parte della semantica di C, fanno parte di C #. Stavo scrivendo per il più ampio contesto dell'agnosticismo linguistico.
Berin Loritsch,

Penso che l'argomento "spreco di memoria" sia controverso. In-lining e tree shaking è un passaggio praticamente universale in qualsiasi ottimizzatore di modalità di rilascio.
Alexander - Ripristina Monica l'

@Alexander, sono d'accordo. È qualcosa di cui essere consapevoli però.
Berin Loritsch,

1
"Le costanti occupano spazio nell'applicazione" - a meno che non si stia sviluppando un'applicazione incorporata per un microcontrollore con solo un kilobyte o due di memoria, non si dovrebbe nemmeno pensare a queste cose.
vsz

2

Il const, #defineo finalè un suggerimento del compilatore (nota che #definenon è in realtà un suggerimento, è una macro e significativamente più potente). Indica che il valore non cambierà durante l'esecuzione di un programma e possono essere fatte varie ottimizzazioni.

Tuttavia, come suggerimento del compilatore, il compilatore fa cose che il programmatore potrebbe non aspettarsi sempre. In particolare, javac incorporerà a in static final int FOO = 42;modo che ovunque FOOvenga utilizzato, il codice byte compilato effettivo leggerà 42.

Questa non è una grande sorpresa fino a quando qualcuno non modifica il valore senza ricompilare l'altra unità di compilazione (file .java) - e i 42resti nel codice byte (vedi è possibile disabilitare l'inserimento di variabili finali statiche di javac? ).

Fare qualcosa static finalsignifica che è così e sempre di più lo sarà e cambiarlo è un vero affare, soprattutto se non altro private.

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


per piccoli valori di ZERO.

3
is a problem and should be examined with great care for where it is used.Perché è un problema?
Alexander - Ripristina Monica l'

1

Come suggerisce il nome, le costanti non dovrebbero cambiare durante il tempo di esecuzione e secondo me le costanti sono definite per non cambiare a lungo termine ( per ulteriori informazioni, consultare questa domanda SO .

Quando si tratta della necessità di flag (ad es. Per la modalità di sviluppo), è necessario invece utilizzare un file di configurazione o un parametro di avvio (molti IDE supportano la configurazione dei parametri di avvio in base al progetto; fare riferimento alla documentazione pertinente) per abilitare questa modalità - in questo modo mantieni la flessibilità di utilizzare tale modalità e non puoi dimenticare di cambiarla ogni volta che il codice diventa produttivo.


0

Essere in grado di essere cambiati tra le esecuzioni è uno dei punti più importanti per la definizione di 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 il ciclo di vita del codice sorgente. È anche una promessa che cambiare la costante in questa posizione cambierà effettivamente tutte le occorrenze di ciò che rappresenta.

Per fare un esempio negativo: sarebbe non senso avere una costante TRUE, che evalutes ad truein una lingua che ha in realtà la trueparola chiave. Non dichiareresti mai, nemmeno una volta, TRUE=falsese non come uno scherzo crudele.

Naturalmente ci sono altri usi delle costanti, ad esempio abbreviare il codice ( CO_NAME = 'My Great World Unique ACME Company'), evitare la duplicazione ( PI=3.141), impostare convenzioni ( TRUE=1) o altro, ma avere una posizione definita per cambiare la costante è sicuramente uno dei più importanti.

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.