Java supports the use of various types of literals, such as integers (5, 2), floating point numbers (2.5, 6.022e+23), characters ('a', '\n'), booleans ('true', 'false'), and strings ("Hello\n"). Extensive use of literals within a program can lead to two problems: first, the meaning of the literal is often obscured or unclear from the context (magic numbers), and second, changing a frequently-used literal requires the entire program source code to be searched for occurrences of that literal, creating possible error sources if some of the occurrences are overlooked.
A solution to these problems is to declare meaningfully-named constants as class variables. Their values can be set to the desired literals, and these constants may be referenced throughout the program rather than inserting the literals themselves. The advantages of this approach are that the constant's name can clearly indicate its meaning or intended use, and should the constant need to be changed, its declaration can be modified without having to search the entire code for all its occurrences.
final
The final
keyword in Java is used to declare constants. Its effect is to render the affected non-composite variable immutable. Attempting to change the value of a final
-qualified variable results in a compile-time error. As constants cannot be changed, it is desirable to define only one instance of them for the class; consequently constants should also be declared with the static
modifier. (DCL31-J. Qualify mathematical constants with the static and final modifiers)
The following code fragment demonstrates the use of static
and final
to create a constant:
private static final int SIZE = 25;
This code snippet declares the value SIZE
to be of the type int
and assigns 25 to it. This constant can subsequently be used wherever the value 25 is needed.
Although final
is most often safe for creating compile time immutable constants, its use has a few caveats when dealing with composite objects (mutable data in particular). See OBJ03-J. Be aware that a final reference may not always refer to immutable data for more details.
Noncompliant Code Example
This noncompliant code example calculates dimensions of a sphere, given its radius.
double area(double radius) { return 12.56 * radius*radius; } double volume(double radius) { return 4.19 * radius * radius * radius; } double greatCircleCircumference(double radius) { return 6.28 * radius; }
The methods use the seemingly-random literals 12.56, 4.19, and 6.28 to represent various scaling factors used to calculate these dimensions. Someone reading this code would have little idea about how they were generated or what they mean, and would consequently be unable to understand the function of this code.
Noncompliant Code Example
This noncompliant code attempts to avoid the above issues by explicitly calculating the required constants
double area(double radius) { return 4.0 * 3.14 * radius * radius; } double volume(double radius) { return 4.0/3.0 * 3.14 * radius * radius * radius; } double greatCircleCircumference(double radius) { return 2 * 3.14 * radius; }
The code uses the literal 3.14 to represent the value pi
. Although this removes some of the ambiguity from the literals, it complicates code maintenance. If the programmer were to decide that a more precise value of pi
is desired, all occurrences of 3.14 in the code would have to be found and replaced.
Compliant Solution
In this compliant solution, a constant PI
is first declared and initialized to 3.14. Thereafter, it is referenced in the code whenever the value of pi
is needed.
private static final int PI = 3.14; double area(double radius) { return 4.0 * PI * radius * radius; } double volume(double radius) { return 4.0/3.0 * PI * radius * radius * radius; } double greatCircleCircumference(double radius) { return 2 * PI * radius; }
This reduces clutter and promotes maintainability, for if a different value of pi
is required, the programmer can simply redefine the constant.
Compliant Solution: Predefined Constants
The class java.lang.Math
defines a large set of numeric constants, such as PI
and the exponential constant E
. If such constants exists, it is preferable to use them rather than redefining their values.
double area(double radius) { return 4.0 * Math.PI * radius * radius; } double volume(double radius) { return 4.0/3.0 * Math.PI * radius * radius * radius; } double greatCircleCircumference(double radius) { return 2 * Math.PI * radius; }
Exceptions
EX1: The use of symbolic constants should be restricted to cases where they improve the readability and maintainability of the code. Using them when the intent of the literal is obvious, or where the literal is not likely to change, can impair code readability. In the preceding compliant solution, the values 4.0 and 3.0 in the volume calculation are clearly scaling factors used to calculate the circle volume, and as such are not subject to change (unlike pi
, they can be represented exactly; there is no reason to change them to increase precision). Hence, replacing them with symbolic constants is inappropriate.
Risk Assessment
Using numeric literals makes code more difficult to read, understand and edit.
Recommendation |
Severity |
Likelihood |
Remediation Cost |
Priority |
Level |
---|---|---|---|---|---|
DCL03- J |
low |
unlikely |
high |
P1 |
L3 |
Related Vulnerabilities
Search for vulnerabilities resulting from the violation of this rule on the CERT website.
Other Languages
This rule appears in the C++ Secure Coding Standard as DCL06-CPP. Use meaningful symbolic constants to represent literal values in program logic and in the C Secure Coding Standard as DCL06-C. Use meaningful symbolic constants to represent literal values in program logic.
References
[[API 06]]
[[Core Java 04]]
DCL02-J. Do not overload variable argument methods 03. Declarations and Initialization (DCL) DCL01-J. Do not declare more than one variable per declaration