¿Se puede cambiar el valor de una constante con el tiempo?

28

Durante la fase de desarrollo, hay ciertas variables que deben corregirse en la misma ejecución, pero que pueden modificarse con el tiempo. Por ejemplo, booleanpara señalar el modo de depuración, por lo que hacemos cosas en el programa que normalmente no haríamos.

¿Es un mal estilo contener estos valores en una constante, es decir, final static int CONSTANT = 0en Java? Sé que una constante se mantiene igual durante el tiempo de ejecución, pero ¿también se supone que es la misma durante todo el desarrollo, excepto los cambios no planificados, por supuesto?

Busqué preguntas similares, pero no encontré nada que coincidiera exactamente con el mío.

GregT
fuente
11
Tengo curiosidad, ¿por qué crees que es un mal estilo cambiar esto?
Vincent Savard el
36
A menos que esté modelando propiedades físicas con constantes que tengan valores matemáticos conocidos, todo puede cambiar en algún momento.
Berin Loritsch
19
El software es suave .
Erik Eidt
10
@GregT No estaría de acuerdo. finalle da una garantía forzada por el compilador de que el programa no modificará el valor. No prescindiría de eso solo porque el programador podría querer modificar el valor asignado en el código fuente.
Alexander - Restablece a Mónica el
10
No hay mucho tiempo para articular una respuesta completa, pero sospecho que la preocupación de sus colegas no es tanto con las constantes, sino con el código en línea de los valores de configuración, que pueden tender a manifestarse como constantes. ... Las constantes te protegen contra errores estúpidos, como asignar accidentalmente a gravitymitad del juego / carrera. No significan necesariamente, gravityes lo mismo en todos los planetas ... Dicho esto, la solución saludable es hacer gravityuna constante, pero extraerla de un planetarchivo o base de datos al comienzo del alcance relevante.
svidgen

Respuestas:

6

En Java, el compilador puede copiar las constantes finales estáticas, como sus valores, en el código que las utiliza . Como resultado de esto, si libera una nueva versión de su código, y hay alguna dependencia descendente que ha utilizado la constante, la constante en ese código no se actualizará a menos que se recompile el código descendente. Esto puede ser un problema si luego hacen uso de esa constante con el código que espera el nuevo valor, ya que aunque el código fuente es correcto, el código binario no lo es.

Esta es una verruga en el diseño de Java, ya que es uno de los pocos casos (tal vez el único caso) donde la compatibilidad de origen y la compatibilidad binaria no son las mismas. Excepto en este caso, puede intercambiar una dependencia con una nueva versión compatible con API sin que los usuarios de la dependencia tengan que volver a compilar. Obviamente, esto es extremadamente importante dada la forma en que generalmente se gestionan las dependencias de Java.

Para empeorar las cosas, el código hará silenciosamente lo incorrecto en lugar de producir errores útiles. Si reemplazara una dependencia con una versión con definiciones de clase o método incompatibles, obtendría errores de invocación o cargador de clases, que al menos proporcionan buenas pistas sobre cuál es el problema. A menos que haya cambiado el tipo de valor, este problema solo aparecerá como un misterioso mal comportamiento en tiempo de ejecución.

Más molesto es que las JVM de hoy podrían alinear fácilmente todas las constantes en tiempo de ejecución sin penalización de rendimiento (aparte de la necesidad de cargar la clase que define la constante, que probablemente se está cargando de todos modos), desafortunadamente la semántica de la fecha del idioma de los días anteriores a los JIT . Y no pueden cambiar el idioma porque el código compilado con compiladores anteriores no será correcto. La compatibilidad con errores ataca de nuevo.

Debido a todo esto, algunas personas aconsejan no cambiar nunca un valor final estático. Para las bibliotecas que pueden distribuirse ampliamente y actualizarse de manera desconocida en momentos desconocidos, esta es una buena práctica.

En su propio código, especialmente en la parte superior de la jerarquía de dependencia, probablemente saldrá con la suya. Pero en estos casos, considere si realmente necesita que la constante sea pública (o protegida). Si la constante es solo visibilidad del paquete, es razonable, dependiendo de sus circunstancias y estándares de código, que todo el paquete siempre se recompilará de una vez, y el problema desaparecerá. Si la constante es privada, no tiene ningún problema y puede cambiarla cuando lo desee.

pelusa
fuente
85

Cualquier cosa en su código fuente, incluidas constlas constantes globales declaradas, puede estar sujeta a cambios con una nueva versión de su software.

Las palabras clave const(o finalen Java) están ahí para indicarle al compilador que esta variable no cambiará mientras se ejecuta esta instancia del programa . Nada mas. Si desea enviar mensajes al próximo responsable de mantenimiento, use un comentario en la fuente, para eso están allí.

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

Es una manera mucho mejor de comunicarse con su futuro yo.

nvoigt
fuente
11
Realmente me gustó este comentario para el futuro yo.
GregT
44
TaxRateser publicme pone nervioso. Me gustaría saber con certeza que solo el departamento de ventas se ve afectado por este cambio y no también nuestros proveedores que nos cobran un impuesto. Quién sabe lo que pasó en la base del código desde que se escribió ese comentario
candied_orange
3
@IllusiveBrian no criticaba el uso de constantes. Estaba advirtiendo contra confiar en un comentario para estar al día. Siempre asegúrese de cómo se usa algo antes de cambiarlo.
candied_orange
8
Este es un buen consejo para Java . Puede ser diferente en otros idiomas. Por ejemplo, debido a la forma en que los valores constantes están vinculados al sitio de la llamada en C #, los public constcampos solo deben usarse para cosas que nunca cambiarán, como Math.pi. Si está creando una biblioteca, las cosas que podrían cambiar durante el desarrollo o con una nueva versión deberían serlo public static readonly, para no causar problemas con los usuarios de su biblioteca.
GrandOpener el
66
Debería elegir un ejemplo diferente ... ¡los valores monetarios nunca deben ser de coma flotante!
corsiKa
13

Necesitamos distinguir dos aspectos de las constantes:

  • nombres para valores conocidos en el momento del desarrollo, que presentamos para una mejor mantenibilidad, y
  • valores que están disponibles para el compilador.

Y luego hay un tercer tipo relacionado: variables cuyo valor no cambia, es decir, nombres para un valor. La diferencia entre estas variables inmutables y una constante es cuando se determina / asigna / inicializa el valor: una variable se inicializa en tiempo de ejecución, pero el valor de una constante se conoce durante el desarrollo. Esta distinción es un poco confusa ya que un valor puede ser conocido durante el desarrollo, pero en realidad solo se crea durante la inicialización.

Pero si el valor de una constante se conoce en tiempo de compilación, entonces el compilador puede realizar cálculos con ese valor. Por ejemplo, el lenguaje Java tiene el concepto de expresiones constantes . Una expresión constante es cualquier expresión que consiste solo en literales de primitivas o cadenas, operaciones en expresiones constantes (como conversión, suma, concatenación de cadenas) y variables constantes. [ JLS §15.28 ] Una variable constante es una finalvariable que se inicializa con una expresión constante. [JLS §4.12.4] Entonces, para Java, esta es una constante de tiempo de compilación:

public static final int X = 7;

Esto se vuelve interesante cuando se usa una variable constante en múltiples unidades de compilación, y luego se cambia la declaración. 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; }

Ahora, cuando compilamos estos archivos, el B.classbytecode declarará un campo Y = 9porque B.Yes una variable constante.

Pero cuando cambiamos la A.Xvariable a un valor diferente (digamos X = 0) y volvemos a compilar solo el A.javaarchivo, B.Yaún se refiere al valor anterior. Este estado A.X = 0, B.Y = 9es inconsistente con las declaraciones en el código fuente. ¡Feliz depuración!

Esto no significa que las constantes nunca deban cambiarse. Las constantes son definitivamente mejores que los números mágicos que aparecen sin explicación en el código fuente. Sin embargo, el valor de las constantes públicas es parte de su API pública . Esto no es específico de Java, pero también ocurre en C ++ y otros lenguajes que cuentan con unidades de compilación separadas. Si cambia estos valores, deberá volver a compilar todo el código dependiente, es decir, realizar una compilación limpia.

Dependiendo de la naturaleza de las constantes, podrían haber llevado a suposiciones incorrectas por parte de los desarrolladores. Si se cambian estos valores, podrían desencadenar un error. Por ejemplo, se puede elegir un conjunto de constantes para que formen ciertos patrones de bits, por ejemplo public static final int R = 4, W = 2, X = 1. Si se cambian para formar una estructura diferente, como el R = 0, W = 1, X = 2código existente, como se boolean canRead = perms & Rvuelve incorrecto. ¡Y solo piense en la diversión que se produciría si Integer.MAX_VALUEcambiaran! Aquí no hay una solución, es importante recordar que el valor de algunas constantes es realmente importante y no se puede cambiar simplemente.

Pero para la mayoría de las constantes, cambiarlas estará bien siempre que se tengan en cuenta las restricciones anteriores. Es seguro cambiar una constante cuando el significado, no el valor específico, es importante. Este es, por ejemplo, el caso de los sintonizables como BORDER_WIDTH = 2o TIMEOUT = 60; // secondsplantillas como API_ENDPOINT = "https://api.example.com/v2/", aunque podría decirse que algunos o todos deberían especificarse en los archivos de configuración en lugar de en el código.

amon
fuente
55
Me gusta este analisis. Lo leí como: eres libre de cambiar una constante siempre y cuando entiendas cómo se usa.
candied_orange
+1 C # también "sufre" el mismo problema con las constantes públicas.
Reginald Blue el
6

Una constante solo se garantiza que sea constante durante la vida útil del tiempo de ejecución de la aplicación . Mientras eso sea cierto, no hay razón para no aprovechar la función del lenguaje. Solo necesita saber cuáles son las consecuencias de usar un indicador constante frente a un compilador para el mismo propósito:

  • Las constantes ocupan espacio de aplicación
  • Las banderas del compilador no
  • El código desactivado por las constantes se puede actualizar y cambiar con herramientas de refactorización modernas
  • El código desactivado por los indicadores del compilador no puede

Habiendo mantenido una aplicación que activaría el dibujo de cuadros delimitadores para formas para que pudiéramos depurar cómo fueron dibujados, nos encontramos con un problema. Después de refactorizar, todo el código que fue desactivado por los indicadores del compilador no se compilaría. Después de eso, cambiamos intencionalmente nuestros indicadores de compilación a constantes de aplicación.

Lo digo para demostrar que hay compensaciones. El peso de unos pocos booleanos no iba a hacer que la aplicación se quedara sin memoria o ocupara demasiado espacio. Eso podría no ser cierto si su constante es realmente un objeto grande que esencialmente tiene un identificador para todo en su código. Si no tiene cuidado de eliminar todas las referencias que contiene a un objeto después de que ya no sea necesario, entonces su objeto puede ser la fuente de una pérdida de memoria.

Debe evaluar el caso de uso y por qué desea cambiar las constantes.

No soy fanático de las simples declaraciones generales, pero en general su colega principal es correcto. Si algo va a cambiar con frecuencia, es posible que deba ser un elemento configurable. Por ejemplo, probablemente podría convencer a su colega de una constante IsInDebugMode = truecuando desee proteger algún código para que no se rompa. Sin embargo, es posible que algunas cosas necesiten cambiar con más frecuencia de lo que publica una aplicación. Si ese es el caso, necesita una forma de cambiar ese valor en el momento adecuado. Puedes tomar el ejemplo de a TaxRate = .065. Eso puede ser cierto en el momento en que compila su código, pero debido a las nuevas leyes, puede cambiar antes de lanzar la próxima versión de su aplicación. Eso es algo que debe actualizarse desde algún mecanismo de almacenamiento (como un archivo o una base de datos)

Berin Loritsch
fuente
¿Qué quieres decir con "banderas compiladoras"? ¿Quizás el preprocesador C y las características similares del compilador que admiten macros / define y #ifdefs? Dado que estos se basan en la sustitución textual del código fuente, no forman parte de la semántica del lenguaje de programación. Tenga en cuenta que Java no tiene un preprocesador.
amon
@amon, Java puede que no, pero varios idiomas sí. Me refiero a #ifdefbanderas. Si bien no son parte de la semántica de C, son parte de C #. Estaba escribiendo para el contexto más amplio del agnosticismo lingüístico.
Berin Loritsch
Creo que el argumento de "pérdida de memoria" es discutible. La alineación y el movimiento de los árboles es un paso bastante universal en cualquier optimizador de modo de lanzamiento.
Alexander - Restablece a Mónica el
@Alexander, estoy de acuerdo. Sin embargo, es algo a tener en cuenta.
Berin Loritsch
1
"Las constantes ocupan espacio en la aplicación": a menos que esté desarrollando una aplicación integrada para un microcontrolador con solo un kilobyte o dos de memoria, ni siquiera debería pensar en esas cosas.
vsz
2

El const, #defineo finales una pista del compilador (tenga en cuenta que #defineno es realmente una pista, es una macro y significativamente más potente). Indica que el valor no cambiará durante la ejecución de un programa y que se pueden realizar varias optimizaciones.

Sin embargo, como sugerencia del compilador, el compilador hace cosas que el programador no siempre puede esperar. En particular, javac incluirá una línea static final int FOO = 42;para que, en cualquier lugar que FOOse utilice, se lea el código de bytes compilado real42 .

Esto no es una gran sorpresa hasta que alguien cambie el valor sin volver a compilar la otra unidad de compilación (archivo .java) y los 42restos en el código de bytes (¿ es posible deshabilitar la inclusión de variables finales estáticas en javac? ).

Hacer algo static finalsignifica que es eso y para siempre será eso, y cambiarlo es realmente una gran cosa, especialmente si es algo diferente private.

Las constantes para cosas como final static int ZERO = 0no son un problema. final static double TAX_RATE = 0.55(aparte de ser dinero y doble es malo y debería usar BigDecimal, pero no es primitivo y, por lo tanto, no está en línea) es un problema y debe examinarse con mucho cuidado para saber dónde se usa.

usuario292808
fuente
para pequeños valores de CERO.
3
is a problem and should be examined with great care for where it is used.Por qué es un problema?
Alexander - Restablece a Mónica el
1

Como su nombre lo indica, las constantes no deberían cambiar durante el tiempo de ejecución y, en mi opinión, las constantes están definidas para que no cambien a largo plazo (puede consultar esta pregunta SO para obtener más información.

Cuando se trata de la necesidad de indicadores (por ejemplo, para el modo de desarrollo), en su lugar, debe usar un archivo de configuración o un parámetro de inicio (muchos IDE admiten la configuración del parámetro de inicio en una base por proyecto; consulte la documentación relevante) para habilitar este modo: De esta forma, mantiene la flexibilidad de utilizar dicho modo y no puede olvidarse de cambiarlo cada vez que el código se vuelve productivo.

DMuenstermann
fuente
0

¡Ser capaz de cambiar entre ejecuciones es uno de los puntos más importantes para definir una constante en su código fuente!

La constante le brinda una ubicación bien definida y documentada (en cierto sentido) para cambiar el valor siempre que lo necesite durante la vida útil de su código fuente. También es una promesa de que cambiar la constante en esta ubicación realmente cambiará todas las ocurrencias de lo que representa.

A modo de ejemplo adversa: sería no tener sentido para tener una constante TRUE, que dé como resultado trueen un idioma que en realidad tiene la truepalabra clave. Nunca, nunca, ni una sola vez, declararías TRUE=falseexcepto como una broma cruel.

Por supuesto, hay otros usos de las constantes, por ejemplo, acortar el código ( CO_NAME = 'My Great World Unique ACME Company'), evitar la duplicación ( PI=3.141), establecer convenciones ( TRUE=1) o lo que sea, pero tener una posición definida para cambiar la constante es sin duda uno de los más destacados.

AnoE
fuente