Are you aware of this usage of final keyword in java?
Tags: java, learning, coding, cleancode
Published: 2023-10-10 09:45:18

Most of us are aware of `final` keyword in java for the following:

1. Prevent Inheritance by declaring class as `final`.
2. Prevent method overriding by making method as `final`.
3. Declaring constants.

I am here to let know more advantages of `final` keyword which are not so generally aware of:

Primitive types like int, char, float etc. other than arrays are substituted at compile time with their values, if you change a `final` that is used by other classes, you must remember to recompile those other classes or your change will not take effect. The same rule applies to constants of type `java.lang.String`. Although String is a constructed type, it is also substituted at compile time. All constructed types other than String, mutable or not, are not substituted at compile time. To understand how this works

**Consider the following code fragment:**

```java
package com.dapinder.practice;

public class FinalConstantSubstitute {

 /** A string constant */
 public final static String STRING_VAR = "Dapinder's Blog";

 /** An int constant. */
 public final static int INT_VAR = 15;

 /** A double constant. */
 public final static double DOUBLE_VAR = 13.23d;

 /** An array constant. */
 public final static int[] ARRAY_VAR = new int[] { 2, 2, 2, 2, 2, 2 };

 /** A Wrapper constant. */
 public final static Integer A_OTHER_WRAPPER = new Integer(10);

 public static void main(String[] args) {

  System.out.println(STRING_VAR);
  System.out.println(INT_VAR);
  System.out.println(DOUBLE_VAR);
  System.out.println(ARRAY_VAR);
  System.out.println(A_OTHER_WRAPPER);

 }
}
```

Once the compiler sees a code as above, it optimises it and starts substituting the primitives and `String` objects. 

> `ByteCode` view of the `FinalConstantSubstitute` post as follows: (used java decompiler)


![ByteCode view](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/tdghlphwhqxgyknqe6op.PNG)

Be cautious of using final constants when accessing in some other classes. Since above is a single class so its not getting noticed but if you use the declared public constants in some other class, then changing of constant, you have to recompile the class making call. 

Check below for example:

```java
 package com.dapinder.practice;

 public class FinalConstantSubstituteMain {

 /**
 * The class using declared final constants.
 * 
 * @param args
 */
 public static void main(String[] args) {
  
  /** Substituted values */
  System.out.println(FinalConstantSubstitute.STRING_VAR);
  System.out.println(FinalConstantSubstitute.INT_VAR);
  System.out.println(FinalConstantSubstitute.DOUBLE_VAR);
  System.out.println(FinalConstantSubstitute.ARRAY_VAR);
  System.out.println(FinalConstantSubstitute.A_OTHER_WRAPPER);

 }
}
```

> `ByteCode` view

![ByteCode view](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/dwfg8w7li1yedepovvkp.PNG)

Here compiler will substitute the constant's values from  `FinalConstantSubstitute`. For example, change of constant `STRING_VAR` to some other value say `Google` will not reflect in `FinalConstantSubstituteMain` unless `FinalConstantSubstituteMain` is re-compiled. The reason is obvious since substitution happens at compile time and is done by the compiler.

### Conditional Compilation

This will amaze you when you think how final variables can provide a way where certain lines get conditional compilation based on a particular condition. This can actually save on most debugging code in production and will improve the performance as well.

To understand its power, lets take an example:

```java
package com.dapinder.practice;

public class ConditionalFinal {

 private static boolean isLoggingEnabled = false;

 /**
 * @param args
 */
 public static void main(String[] args) {
  if (isLoggingEnabled) {
   System.out.println("Statment 1");
  }
  if (isLoggingEnabled) {
   System.out.println("Statment 2");
  }
  if (isLoggingEnabled) {
   System.out.println("Statment 3");
  }
  if (isLoggingEnabled) {
   System.out.println("Statment 4");
  }
 }
}
```

Please note that variable `isLoggingEnabled` is not declared `final` here.

> Bytecode view:

![Bytecode](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/fwo3kduvd675dal28eiz.PNG)

Please note that all `if` conditions are very well referencing the variable and if the `isLoggingEnabled` is `true`, i will print on the console and when false it wont print but yet have although less on performance as it will check each conditional IF statement. 

> This is fine and at first glance does not seem expensive but in production where one method is called by millions of threads and multiple times, this can get expensive.

Most of us know and have used Log4J and we know not all logging statements are turned on in production. Most of us even do a `isDebugEnabled` before logging a debug statement.
However When using the `isDebugEnabled( )`, the cost is much more than one comparison per if statement.

Now check by converting the `isLoggingEnabled` variable into a `final` variable:

```java
 package com.dapinder.practice;

public class ConditionalFinal {

 private static final boolean isLoggingEnabled = false;

 /**
 * @param args
 */
 public static void main(String[] args) {
  if (isLoggingEnabled) {
   System.out.println("Statment 1");
  }
  if (isLoggingEnabled) {
   System.out.println("Statment 2");
  }
  if (isLoggingEnabled) {
   System.out.println("Statment 3");
  }
  if (isLoggingEnabled) {
   System.out.println("Statment 4");
  }
 }
}
```

By converting the `isLoggingEnabled` attribute into a `final` attribute, you have told the compiler that whenever it sees `isLoggingEnabled`, it should replace it with false as per substitution principle explained above.


**One more important point here.**

Most of us do get WARNING in any IDE (say Eclipse) whenever a piece of code is unreachable. Here compiler applies the same principle and removes all those statement which are meant to be unreachable or will never execute.

Here, in above example, all these logging statements will disappear.

![Bytecode](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/zkzwpemhkqc35jmv7u41.PNG)