Um valor de uma constante pode ser alterado ao longo do tempo?

28

Durante a fase de desenvolvimento, há certas variáveis ​​que precisam ser corrigidas na mesma execução, mas podem precisar ser modificadas ao longo do tempo. Por exemplo, booleanpara sinalizar o modo de depuração, fazemos coisas no programa que normalmente não faríamos.

É mau estilo conter esses valores em uma constante, ou seja, final static int CONSTANT = 0em Java? Eu sei que uma constante permanece a mesma durante o tempo de execução, mas também deveria ser a mesma durante todo o desenvolvimento, exceto para mudanças não planejadas, é claro?

Procurei perguntas semelhantes, mas não encontrei nada que correspondesse exatamente ao meu.

GregT
fonte
11
Estou curioso, por que você acha que é um estilo ruim mudar isso?
Vincent Savard
36
A menos que você esteja modelando propriedades físicas com constantes que tenham valores matemáticos conhecidos, tudo poderá mudar em algum momento.
Berin Loritsch
19
software é macio .
Erik Eidt
10
@ GregT eu discordo. finalfornece uma garantia imposta pelo compilador de que o programa não modificará o valor. Eu não descartaria isso apenas porque o programador pode querer modificar o valor atribuído no código-fonte.
Alexander - Restabelece Monica
10
Não há muito tempo para articular uma resposta completa, mas suspeito que a preocupação de seus colegas não é tanto com constantes, mas com a inserção de código dos valores de configuração, que podem tender a se manifestar como constantes. ... As constantes protegem você contra erros estúpidos, como atribuir acidentalmente no gravitymeio do jogo / corrida. Eles não significam necessariamente que gravityé o mesmo em todos os planetas ... Dito isto, a solução saudável é criar gravityuma constante, mas extraí-la de um planetarquivo ou banco de dados no início do escopo relevante.
Svidgen

Respostas:

6

Em Java, as constantes finais estáticas podem ser copiadas, pelo compilador, como seus valores, no código que as utiliza . Como resultado disso, se você lançar uma nova versão do seu código e houver alguma dependência a jusante que usou a constante, a constante nesse código não será atualizada, a menos que o código a jusante seja recompilado. Isso pode ser um problema se eles fizerem uso dessa constante com código que espera o novo valor, pois mesmo que o código-fonte esteja correto, o código binário não esteja.

Essa é uma verruga no design do Java, já que é um dos poucos casos (talvez o único) em que a compatibilidade de origem e a compatibilidade binária não são iguais. Exceto nesse caso, você pode trocar uma dependência por uma nova versão compatível com a API, sem que os usuários da dependência precisem recompilar. Obviamente, isso é extremamente importante, dada a maneira como as dependências Java geralmente são gerenciadas.

Para piorar a situação, o código fará silenciosamente a coisa errada, em vez de produzir erros úteis. Se você substituísse uma dependência por uma versão com definições incompatíveis de classe ou método, obteria erros no carregador de classes ou na chamada, que pelo menos fornecem boas pistas sobre qual é o problema. A menos que você tenha alterado o tipo do valor, esse problema aparecerá apenas como um comportamento inadequado do tempo de execução misterioso.

O mais irritante é que as JVMs de hoje podem alinhar facilmente todas as constantes em tempo de execução sem penalidade no desempenho (além da necessidade de carregar a classe que define a constante, que provavelmente está sendo carregada de qualquer maneira), infelizmente a semântica do idioma data dos dias anteriores às JITs . E eles não podem mudar o idioma, porque o código compilado com os compiladores anteriores não estará correto. A compatibilidade com erros ocorre novamente.

Por causa disso tudo, algumas pessoas aconselham nunca alterar um valor final estático. Para bibliotecas que podem ser amplamente distribuídas e atualizadas de maneiras desconhecidas em momentos desconhecidos, essa é uma boa prática.

Em seu próprio código, especialmente no topo da hierarquia de dependências, você provavelmente se safará disso. Mas nesses casos, considere se você realmente precisa que a constante seja pública (ou protegida). Se a constante for apenas visibilidade do pacote, é razoável, dependendo de suas circunstâncias e padrões de código, que o pacote inteiro seja sempre recompilado de uma só vez, e o problema desaparecerá. Se a constante for privada, você não terá nenhum problema e poderá alterá-la quando quiser.

fluffysheap
fonte
85

Qualquer coisa no seu código-fonte, incluindo constconstantes globais declaradas, pode estar sujeita a alterações com uma nova versão do seu software.

As palavras-chave const(ou finalem Java) estão lá para sinalizar ao compilador que essa variável não será alterada enquanto esta instância do programa estiver em execução . Nada mais. Se você deseja enviar mensagens para o próximo mantenedor, use um comentário na fonte, é para isso que elas servem.

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

É uma maneira muito melhor de se comunicar com seu futuro eu.

nvoigt
fonte
11
Eu realmente gostei deste comentário para o futuro eu.
GregT
4
TaxRateestar publicme deixa nervoso. Gostaria de ter certeza de que apenas o departamento de vendas foi impactado por essa alteração e não também nossos fornecedores que nos cobram um imposto. Quem sabe o que aconteceu na base de código desde que esse comentário foi escrito.
candied_orange
3
@IllusiveBrian não estava criticando o uso de constantes. Foi um aviso contra confiar em um comentário para estar atualizado. Sempre certifique-se de como algo é usado antes de alterá-lo.
candied_orange
8
Este é um bom conselho para Java . Pode ser diferente em outros idiomas. Por exemplo, devido à maneira como os valores const são vinculados ao site de chamada em C #, os public constcampos devem ser usados apenas para coisas que nunca serão alteradas, como Math.pi. Se você estiver criando uma biblioteca, as coisas que podem mudar durante o desenvolvimento ou com uma nova versão devem ser public static readonly, para não causar problemas aos usuários da sua biblioteca.
GrandOpener
6
Você deve escolher um exemplo diferente ... os valores monetários nunca devem ser um ponto flutuante!
corsiKa
13

Precisamos distinguir dois aspectos das constantes:

  • nomes de valores conhecidos no momento do desenvolvimento, que apresentamos para melhor manutenção, e
  • valores que estão disponíveis para o compilador.

E depois há um terceiro tipo relacionado: variáveis ​​cujo valor não muda, ou seja, nomes para um valor. A diferença entre essas variáveis ​​imutáveis ​​e uma constante é quando o valor é determinado / atribuído / inicializado: uma variável é inicializada em tempo de execução, mas o valor de uma constante é conhecido durante o desenvolvimento. Essa distinção é um pouco confusa, pois um valor pode ser conhecido durante o desenvolvimento, mas na verdade é criado apenas durante a inicialização.

Mas se o valor de uma constante for conhecido no tempo de compilação, o compilador poderá executar cálculos com esse valor. Por exemplo, a linguagem Java tem o conceito de expressões constantes . Uma expressão constante é qualquer expressão que consiste apenas em literais de primitivas ou cadeias, operações em expressões constantes (como conversão, adição, concatenação de cadeias) e em variáveis ​​constantes. [ JLS §15.28 ] Uma variável constante é uma finalvariável que é inicializada com uma expressão constante. [JLS §4.12.4] Então, para Java, esta é uma constante em tempo de compilação:

public static final int X = 7;

Isso se torna interessante quando uma variável constante é usada em várias unidades de compilação e, em seguida, a declaração é alterada. Considerar:

  • A.java:

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

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

Agora, quando compilarmos esses arquivos, o B.classbytecode declarará um campo Y = 9porque B.Yé uma variável constante.

Mas quando alteramos a A.Xvariável para um valor diferente (digamos X = 0) e recompilamos apenas o A.javaarquivo, B.Yainda se refere ao valor antigo. Este estado A.X = 0, B.Y = 9é inconsistente com as declarações no código fonte. Feliz depuração!

Isso não significa que as constantes nunca devem ser alteradas. As constantes são definitivamente melhores que os números mágicos que aparecem sem explicação no código fonte. No entanto, o valor de constantes públicas faz parte da sua API pública . Isso não é específico para Java, mas também ocorre em C ++ e outras linguagens que apresentam unidades de compilação separadas. Se você alterar esses valores, precisará recompilar todo o código dependente, ou seja, executar uma compilação limpa.

Dependendo da natureza das constantes, elas podem ter levado a suposições incorretas pelos desenvolvedores. Se esses valores forem alterados, eles podem acionar um bug. Por exemplo, um conjunto de constantes pode ser escolhido para formar certos padrões de bits, por exemplo public static final int R = 4, W = 2, X = 1. Se eles forem alterados para formar uma estrutura diferente R = 0, W = 1, X = 2, o código existente boolean canRead = perms & Rficará incorreto. E pense na diversão que resultaria Integer.MAX_VALUEem mudar! Não há solução aqui, é apenas importante lembrar que o valor de algumas constantes é realmente importante e não pode ser alterado simplesmente.

Mas para a maioria das constantes alterá-las, tudo ficará bem desde que as restrições acima sejam consideradas. Uma constante é segura para mudar quando o significado, e não o valor específico, é importante. É o caso, por exemplo, de ajustáveis ​​como BORDER_WIDTH = 2ou TIMEOUT = 60; // secondsou modelos como API_ENDPOINT = "https://api.example.com/v2/"- embora, sem dúvida, alguns ou todos eles devam ser especificados nos arquivos de configuração, e não no código.

amon
fonte
5
Eu gosto dessa análise. Eu li como: Você é livre para alterar uma constante desde que entenda como ela é usada.
candied_orange
+1 em C # também "sofre" com o mesmo problema com constantes públicas.
Reginald Blue
6

Uma constante só é garantida como constante durante a vida útil do tempo de execução do aplicativo . Enquanto isso for verdade, não há razão para não tirar proveito do recurso de idioma. Você só precisa saber quais são as consequências para o uso de um sinalizador de constante versus compilador para o mesmo objetivo:

  • As constantes ocupam espaço no aplicativo
  • Os sinalizadores do compilador não
  • O código desativado pelas constantes pode ser atualizado e alterado com as modernas ferramentas de refatoração
  • O código desativado pelos sinalizadores do compilador não pode

Tendo mantido um aplicativo que ativava o desenho de caixas delimitadoras de formas para podermos depurar como elas foram desenhadas, encontramos um problema. Após a refatoração, todo o código desativado pelos sinalizadores do compilador não seria compilado. Depois disso, intencionalmente alteramos nossos sinalizadores de compilador para constantes de aplicativo.

Estou dizendo isso para demonstrar que existem compensações. O peso de alguns booleanos não deixaria o aplicativo sem memória ou ocuparia muito espaço. Isso pode não ser verdade se a sua constante for realmente um objeto grande que essencialmente possui um identificador para tudo no seu código. Se não for necessário remover todas as referências que ele mantém sobre um objeto depois que ele não for mais necessário, seu objeto poderá ser a fonte de um vazamento de memória.

Você precisa avaliar o caso de uso e por que deseja alterar as constantes.

Não sou fã de simples declarações gerais, mas, em geral, seu colega sênior está correto. Se algo estiver sujeito a alterações frequentes, pode ser necessário que seja um item configurável. Por exemplo, você provavelmente poderia convencer seu colega por uma constante IsInDebugMode = truequando quiser proteger algum código contra quebra. No entanto, algumas coisas podem precisar mudar com mais frequência do que você libera um aplicativo. Se for esse o caso, você precisa de uma maneira de alterar esse valor no momento apropriado. Você pode pegar o exemplo de a TaxRate = .065. Isso pode ser verdade no momento em que você compila seu código, mas devido a novas leis, ele pode ser alterado antes do lançamento da próxima versão do seu aplicativo. Isso é algo que precisa ser atualizado a partir de algum mecanismo de armazenamento (como arquivo ou banco de dados)

Berin Loritsch
fonte
O que você quer dizer com "sinalizadores de compilador"? Talvez o pré-processador C e os recursos similares do compilador suportem macros / define #ifdefes? Como eles se baseiam na substituição textual do código fonte, eles não fazem parte da semântica da linguagem de programação. Observe que o Java não possui um pré-processador.
amon
@amon, Java pode não, mas várias linguagens o fazem. Eu quero dizer #ifdefbandeiras. Embora eles não façam parte da semântica de C, eles fazem parte do C #. Eu estava escrevendo para o contexto maior do agnosticismo da linguagem.
Berin Loritsch
Eu acho que o argumento "desperdício de memória" é discutível. O alinhamento e a trepidação de árvores é uma etapa praticamente universal em qualquer otimizador de modo de liberação.
Alexander - Restabelecer Monica
@ Alexander, eu concordo. É algo para estar ciente de que.
Berin Loritsch
11
"As constantes ocupam espaço no aplicativo" - a menos que você esteja desenvolvendo um aplicativo incorporado para um microcontrolador com apenas um kilobyte ou dois de memória, você nem deveria pensar nessas coisas.
vsz
2

A const, #defineou finalé uma dica do compilador (observe que na #defineverdade não é uma dica, é uma macro e significativamente mais poderosa). Isso indica que o valor não será alterado durante a execução de um programa e várias otimizações podem ser feitas.

No entanto, como uma dica do compilador, o compilador faz coisas que nem sempre são esperadas pelo programador. Em particular, o javac alinhará um static final int FOO = 42;para que, em qualquer lugar que FOOseja usado, o código de bytes compilado real seja lido 42.

Isso não é uma surpresa muito grande até que alguém mude o valor sem recompilar a outra unidade de compilação (arquivo .java) - e os 42restos no código de bytes (veja é possível desabilitar o alinhamento de variáveis ​​finais estáticas do javac? ).

Fazer algo static finalsignifica que é isso e para sempre será isso e mudá-lo é realmente um grande negócio - especialmente se não for private.

Constantes para coisas como final static int ZERO = 0não são um problema. final static double TAX_RATE = 0.55(além de ser dinheiro e duplicar é ruim e deve estar usando o BigDecimal, mas não é um primitivo e, portanto, não está embutido) é um problema e deve ser examinado com muito cuidado para onde é usado.

user292808
fonte
para pequenos valores de ZERO.
3
is a problem and should be examined with great care for where it is used.Por que isso é um problema?
Alexander - Restabelece Monica
1

Como o nome sugere, as constantes não devem ser alteradas durante o tempo de execução e, na minha opinião, as constantes são definidas para não serem alteradas por um longo período (você pode consultar esta questão de SO para obter mais informações.

Quando se trata de necessidade de sinalizadores (por exemplo, para o modo de desenvolvimento), você deve usar um arquivo de configuração ou um parâmetro de inicialização (muitos IDEs suportam a configuração de parâmetros de inicialização em uma base por projeto; consulte a documentação relevante) para ativar este modo - Dessa forma, você mantém a flexibilidade de usar esse modo e não se esqueça de alterá-lo sempre que o código for produtivo.

DMuenstermann
fonte
0

Ser capaz de mudar entre as execuções é um dos pontos mais importantes da definição de uma constante no seu código-fonte!

A constante fornece um local bem definido e documentado (em certo sentido) para alterar o valor sempre que necessário durante a vida útil do seu código-fonte. Também é uma promessa que mudar a constante nesse local realmente mudará todas as ocorrências do que quer que seja.

Como um exemplo adverso: não faria sentido ter uma constante TRUEque se avalie trueem um idioma que realmente tenha a truepalavra - chave. Você nunca, nem mesmo uma vez, declararia, TRUE=falseexceto como uma piada cruel.

É claro que existem outros usos de constantes, por exemplo, abreviar código ( CO_NAME = 'My Great World Unique ACME Company'), evitar duplicação ( PI=3.141), definir convenções ( TRUE=1) ou qualquer outra coisa, mas ter uma posição definida para alterar a constante é certamente um dos mais importantes.

AnoE
fonte