28

During development phase, there are certain variables which need to be the fixed in the same run, but may need be modified over time. For example a boolean to signal debug mode, so we do things in the program we normally wouldn't.

Is it bad style to contain these values in a constant, i.e. final static int CONSTANT = 0 in Java? I know that a constant stays the same during run time, but is it also supposed to be the same during the whole development, except for unplanned changes, of course?

I searched for similar questions, but did not find anything that matched mine exactly.

14
  • 11
    I'm curious, why would you believe it to be bad style to change these? Commented Jan 8, 2018 at 13:58
  • 36
    Unless you are modeling physical properties with constants that have known mathematical values, everything can change at some time. Commented Jan 8, 2018 at 14:04
  • 19
    software is soft.
    – Erik Eidt
    Commented Jan 8, 2018 at 15:58
  • 10
    @GregT I would disagree. final gives you a compiler-enforced guarantee that the program won't modify the value. I wouldn't dispense with that just because the programmer might want to modify the value assigned in the source code.
    – Alexander
    Commented Jan 8, 2018 at 18:24
  • 10
    Not much time to articulate a full answer, but I suspect your colleagues concern isn't so much with constants, but with the code-inlining of configuration values, which might tend to manifest as constants. ... Constants protect you against stupid mistakes, like accidentally assigning over gravity mid-game/run. They don't necessarily mean, gravity is the same on every planet ... That said, the healthy solution is to make gravity a constant, but pull it from a planet file or database at the start of the relevant scope.
    – svidgen
    Commented Jan 8, 2018 at 21:02

7 Answers 7

6

In Java, static final constants can be copied, by the compiler, as their values, into code which uses them. As a result of this, if you release a new version of your code, and there is some downstream dependency that has used the constant, the constant in that code will not be updated unless the downstream code is recompiled. This can be a problem if they then make use of that constant with code that expects the new value, as even though the source code is right, the binary code isn't.

This is a wart in the design of Java, since it's one of very few cases (maybe the only case) where source compatibility and binary compatibility aren't the same. Except for this case, you can swap out a dependency with a new API-compatible version without users of the dependency having to recompile. Obviously this is extremely important given the way in which Java dependencies are generally managed.

Making matters worse is that the code will just silently do the wrong thing rather than producing useful errors. If you were to replace a dependency with a version with incompatible class or method definitions, you would get classloader or invocation errors, which at least provide good clues as to what the problem is. Unless you've changed the type of the value, this problem will just appear as mysterious runtime misbehavior.

More annoying is that today's JVMs could easily inline all the constants at runtime without performance penalty (other than the need to load the class defining the constant, which is probably being loaded anyway), unfortunately the semantics of the language date from the days before JITs. And they can't change the language because then code compiled with previous compilers won't be correct. Bugward-compatibility strikes again.

Because of all this some people advise never changing a static final value at all. For libraries which might be distributed widely and updated in unknown ways at unknown times, this is good practice.

In your own code, especially at the top of the dependency hierarchy, you will probably get away with it. But in these cases, consider whether you really need the constant to be public (or protected). If the constant is package-visibility only, it's reasonable, depending on your circumstances and code standards, that the entire package will always be recompiled at once, and the problem then goes away. If the constant is private, you have no problem and can change it whenever you like.

85

Anything in your source code, including const declared global constants, might be subject to change with a new release of your software.

The keywords const (or final in Java) are there to signal to the compiler that this variable will not change while this instance of the program is running. Nothing more. If you want to send messages to the next maintainer, use a comment in source, that's what they are there for.

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

Is a way better way to communicate with your future self.

17
  • 12
    I really liked this comment to the future self.
    – GregT
    Commented Jan 8, 2018 at 14:15
  • 4
    TaxRate being public makes me nervous. I'd like to know for sure that only the sales department in impacted by this change and not also our suppliers who charge us a tax. Who knows what's happened in the code base since that comment was written. Commented Jan 8, 2018 at 20:41
  • 3
    @IllusiveBrian wasn't criticizing use of constants. Was warning against trusting a comment to be up to date. Always be sure of how something is used before you change it. Commented Jan 8, 2018 at 21:40
  • 8
    This is good advice for Java. It may be different in other languages. For example, because of the way const values are bound to the call site in C#, public const fields should only be used for things that will never-ever change, like Math.pi. If you are creating a library, things that might change during development or with a new version should be public static readonly, so as to not cause problems with users of your library. Commented Jan 9, 2018 at 7:22
  • 6
    You should pick a different example... money values should never be floating point!
    – corsiKa
    Commented Jan 9, 2018 at 15:21
13

We need to distinguish two aspects of constants:

  • names for a values known at development time, which we introduce for better maintainability, and
  • values that are available to the compiler.

And then there's a related third kind: variables whose value does not change, i.e. names for a value. The difference between an these immutable variables and a constant is when the value is determined/assigned/initialized: a variable is initialized at runtime, but the value of a constant is known during development. This distinction is a bit muddy since a value may be known during development but is actually only created during initialization.

But if the value of a constant is known at compile-time, then the compiler can perform computations with that value. For example, the Java language has the concept of constant expressions. A constant expression is any expression that consists only of literals of primitives or strings, operations on constant expressions (such as casting, addition, string concatenation), and of constant variables. [JLS §15.28] A constant variable is a final variable that is initialized with a constant expression. [JLS §4.12.4] So for Java, this is a compile-time constant:

public static final int X = 7;

This becomes interesting when a constant variable is used in multiple compilation units, and then the declaration is changed. Consider:

  • A.java:

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

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

Now when we compile these files the B.class bytecode will declare a field Y = 9 because B.Y is a constant variable.

But when we change the A.X variable to a different value (say, X = 0) and recompile only the A.java file, then B.Y still refers to the old value. This state A.X = 0, B.Y = 9 is inconsistent with the declarations in the source code. Happy debugging!

This doesn't mean that constants should never be changed. Constants are definitively better than magic numbers that appear without explanation in the source code. However, the value of public constants is part of your public API. This isn't specific to Java, but also occurs in C++ and other languages that feature separate compilation units. If you change these values, you will need to recompile all dependent code, i.e. perform a clean compile.

Depending on the nature of the constants, they might have led to incorrect assumptions by the developers. If these values are changed, they might trigger a bug. For example, a set of constants might be chosen so that they form certain bit patterns, e.g. public static final int R = 4, W = 2, X = 1. If these are changed to form a different structure like R = 0, W = 1, X = 2 then existing code such as boolean canRead = perms & R becomes incorrect. And just think of the fun that would ensue were Integer.MAX_VALUE to change! There is no fix here, it's just important to remember that the value of some constants really is important and cannot be changed simply.

But for the majority of constants changing them is going to be fine as long as the above restrictions are considered. A constant is safe to change when the meaning, not the specific value is important. This is e.g. the case for tunables such as BORDER_WIDTH = 2 or TIMEOUT = 60; // seconds or templates such as API_ENDPOINT = "https://api.example.com/v2/" – though arguably some or all of those ought to be specified in configuration files rather than code.

2
  • 5
    I like this analysis. I read it as: You are free to change a constant so long as you understand how it's used. Commented Jan 8, 2018 at 17:31
  • +1 C# also "suffers" from the same issue with public constants. Commented Jan 9, 2018 at 13:49
6

A constant is only guaranteed to be constant for the life of the application runtime. As long as that is true, there is no reason to not take advantage of the language feature. You just need to know what the consequences are for using a constant vs. compiler flags for the same purpose:

  • Constants take up application space
  • Compiler flags do not
  • Code turned off by constants can be updated and changed with modern refactoring tools
  • Code turned off by compiler flags cannot

Having maintained an application that would turn on drawing bounding boxes for shapes so that we could debug how they were drawn, we ran into a problem. After refactoring, all the code that was turned off by compiler flags wouldn't compile. After that, we intentionally changed our compiler flags to application constants.

I'm saying that to demonstrate that there are trade-offs. The weight of a few booleans wasn't going to make the application run out of memory or take up too much space. That might not be true if your constant is really a large object that essentially has a handle to everything in your code. If it doesn't take care to remove all references it holds to an object after it is no longer needed, then your object may be the source of a memory leak.

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

I'm not a fan of simple blanket statements, but in general your senior colleague is correct. If something is bound to change often, it might need to be a configurable item. For example, you could probably convince your colleague for a constant IsInDebugMode = true when you want to protect some code from being broken. However, some things may need to change more often than you release an application. If that is the case, you need a way of changing that value at the appropriate time. You can take the example of a TaxRate = .065. That may be true at the time you compile your code, but due to new laws it can change before you release the next version of your application. That is something that needs to be updated either from some storage mechanism (like file or database)

6
  • What do you mean by “compiler flags”? Perhaps the C preprocessor and similar compiler features that support macros/defines and #ifdefs? Since these are based on textual substitution of the source code, they are not part of the programming language semantics. Note that Java does not have a preprocessor.
    – amon
    Commented Jan 8, 2018 at 14:30
  • @amon, Java might not, but several languages do. I do mean #ifdef flags. While they are not part of C's semantics, they are part of C#. I was writing for the larger context of language agnosticism. Commented Jan 8, 2018 at 14:32
  • I think the "memory waste" argument is moot. In-lining and tree shaking is a pretty much universal step in any release-mode optimizer.
    – Alexander
    Commented Jan 8, 2018 at 18:27
  • @Alexander, I agree. It is something to be aware of though. Commented Jan 8, 2018 at 18:42
  • 1
    "Constants take up application space" - unless you are developing an embedded application for a microcontroller with only a kilobyte or two of memory, you shouldn't even be thinking about such things.
    – vsz
    Commented Jan 9, 2018 at 7:07
2

The const, #define or final is a compiler hint (note that the #define isn't actually a hint, its a macro and significantly more powerful). It indicates that the value will not change over the execution of a program and various optimizations may be done.

However, as a compiler hint, the compiler does things that may not always be expected by the programmer. In particular, javac will inline a static final int FOO = 42; so that anywhere that FOO is used, the actual compiled byte code will read 42.

This isn't too big of a surprise until someone changes the value without recompiling the other compilation unit (.java file) - and the 42 remains in the byte code (see is it possible to disable javac's inlining of static final variables?).

Making something static final means that it is that and forever more will be that and changing it is a Really Big Deal - especially if its anything but private.

Constants for things like final static int ZERO = 0 isn't a problem. final static double TAX_RATE = 0.55 (aside from being money and double is bad and should be using BigDecimal, but then its not a primitive and thus not inlined) is a problem and should be examined with great care for where it is used.

2
  • for small values of ZERO.
    – user251748
    Commented Jan 8, 2018 at 18:12
  • 3
    is a problem and should be examined with great care for where it is used. Why is it a problem?
    – Alexander
    Commented Jan 8, 2018 at 18:30
1

As the name suggests, constants should not change during runtime and in my opinion constants are defined to not change for a long term (you may look at this SO question for more information.

When it comes to the need of flags (e.g. for development mode) you should instead use a config file or startup parameter (many IDEs support configuring startup parameter on a per-project-base; refer to the relevant documentation) to enable this mode - this way you keep the flexibility to use such a mode and you cannot forget to change it everytime the code goes productive.

0

Being able to be changed between runs is one of the most important points of defining a constant in your source code!

The constant gives you a well-defined and documented (in a sense) location to change the value whenever you need to during the lifetime of your source code. It is also a promise that changing the constant at this location will actually change all occurences of whathever it stands for.

As an adverse example: it would not make sense to have a constant TRUE which evalutes to true in a language which actually has the true keyword. You would never, ever, not even once, declare TRUE=false except as a cruel joke.

Of course there are other uses of constants, for example shortening code (CO_NAME = 'My Great World Unique ACME Company'), avoiding duplication (PI=3.141), setting conventions (TRUE=1) or whatever, but having a defined position to change the constant is certainly one of the most prominent ones.

Not the answer you're looking for? Browse other questions tagged or ask your own question.