Can things be avoided from getting Inherited? - The 'final' Keyword

Share on:

Overview

We've seen multiple examples of how to inherit properties from parent class to the child classes using extends keyword. We've also seen examples of method overriding and why we need it. Now few questions come to mind: Is there a way to prevent classes from getting extended, is there a way to protect methods from getting overridden and why will someone ever need to do such a thing. Let's dive deep.

How to prevent classes from getting inherited?

We can prevent classes from being extended/inherited by marking the class final. final keyword tells the compiler that this particular class cannot be extended and it is the end of inheritance tree. Code Example:

1final class A {
2
3    public void func() {
4
5        System.out.println("A's func()");
6    }
7}
1class B extends A {
2
3
4}
Compiling above classes will give us the following error:
1B.java:1: error: cannot inherit from final A
2class B extends A {
3                ^
41 error

The compiler error clearly states that we cannot extend a class marked by final keyword. The use of final keyword solves our purpose.

Exploring the final keyword

We've seen in the last section, the use of final keyword at class level. It stops any class from getting extended further. Let's see other uses of this final keyword.

To stop method overriding

A method marked by final keyword tells the compiler that this method should not be overridden down the inheritance tree.

1class A {
2
3    final public void func() {
4
5        System.out.println("A's func()");
6    }
7}
1class B extends A {
2
3    public void func() {
4
5        System.out.println("B's func()");
6    }
7}

The output upon compiling the above two files is:

1B.java:3: error: func() in B cannot override func() in A
2    public void func() {
3                ^
4  overridden method is final
51 error
The compiler clearly states that we cannot override a method marked by final keyword in superclass.
A non-final class can contain final methods. It will mean that the class can be extended but some methods, which were marked by final, cannot be overridden by any subclass. (as shown in our second example)

To create constants

We can create constants in Java by marking our variables with final keyword. By marking variables with final we ensure that the value of this variable will not change throughout the course of the program.

1class HelperClass {
2
3    public static void main(String[] args) {
4
5        final int a = 5;
6        System.out.println("The value of a is: " + a);
7        a = 10;
8    }
9}
Output:
1HelperClass.java:7: error: cannot assign a value to final variable a
2        a = 10;
3        ^
41 error

As we can see, our compiler error clearly states that we cannot change the value of a variable marked by final. Therefore, final keyword ensures that 'a' will remain same throughout the course of the program.

What about reference variables?

Yes, we can mark reference variables with final keyword as well. Let's see what happens in this case

1class A {
2
3    public int a = 5;
4}
1class HelperClass {
2
3    public static void main(String[] args) {
4
5        final A a1 = new A();
6        A a2 = new A();
7        a1 = a2;
8    }
9}
Output:
1HelperClass.java:7: error: cannot assign a value to final variable a1
2        a1 = a2;
3        ^
41 error
Yet again, our compiler error message is clear enough stating that once a reference variable is marked final, we cannot change what it refers to i.e. it cannot refer to something else. Notice the last statement: Once a reference variable is marked final, it cannot refer to something else. If we change our HelperClass.java to the following:
 1class HelperClass {
 2
 3    public static void main(String[] args) {
 4
 5        final A a1 = new A();
 6        System.out.println("a1'a value: " + a1.a);
 7
 8        a1.a += 5;
 9        System.out.println("a1'a value now: " + a1.a);
10    }
11}
Want to take a guess what will happen? Here's the output:
1a1'a value: 5
2a1'a value now: 10
Let's take a minute to digest what has happened here. Once a reference variable is marked final we cannot refer it to something else, we've already established this much. But by using a final reference variable, we are able to change the internal state of the object because by doing so we are not exactly "changing the value of our reference variable". The value of our reference variable is still the same, it still contains the bits to reach our object in heap.

Marking a1 as final only tells the compiler that the value of a1 will not change throughout the course of our program. As a1 is a reference variable, it means that a1 will not point to something else. But the final keyword doesn't say that we cannot change the internal state of the object a1 is referring to. Hence the above program is completely legit.

Similar logic will apply to arrays declared as final. As arrays are treated like objects in Java once an array is marked as final, that particular variable cannot point to a different array object. But we still can change the values of our array. Here's an example:
1class HelperClass {
2
3    public static void main(String[] args) {
4
5        final int[] arr1 = {1, 2, 3, 4, 5};
6        int[] arr2 = {6, 7, 8, 9, 10};
7        arr1 = arr2;
8    }
9}
This program will throw us the following error:
1HelperClass.java:7: error: cannot assign a value to final variable arr1
2        arr1 = arr2;
3        ^
41 error
But the following program is completely legit:
 1class HelperClass {
 2
 3    public static void main(String[] args) {
 4
 5        final int[] arr1 = {1, 2, 3, 4, 5};
 6        System.out.println("Printing array");
 7        for (int a : arr1) {
 8
 9            System.out.print(a + " ");
10        }
11        System.out.println("");
12
13        arr1[3] = 10;
14        System.out.println("Printing array");
15        for (int a : arr1) {
16
17            System.out.print(a + " ");
18        }
19        System.out.println("");
20    }
21}
And gives us the following output:
1Printing array
21 2 3 4 5 
3Printing array
41 2 3 10 5 
I hope the behavior of final keyword on reference variables in now clear. This behavior opens up a new discussion topic.

final v/s Immutability

Immutability means that I cannot change the internal state of my object. Strings are immutable, I cannot the change the value of a string.

1String str = "Immutable String";
2str[4] = 'I';       /*illegal*/
This is because String class is Immutable. We cannot change the "internal state" of String's objects.

This is different from final. Marking variable final tells the compiler that we cannot change the value of that particular variable.
1final int a = 4;
2a = 5;      /*Illegal*/
3final String str = "Immutable String";
4String str2 = "Another String";
5str = str2;         /*Illegal*/

But using final keyword we can change the internal state of a mutable object (example shown above). And similarly cannot change the internal state of immutable object even with a non-final variable (string example)
Here's a pictorial explanation: Final v/s Immutable

I hope this fine distinction between 'final' and 'Immutability' is clear by now.

Takeaways

  • Various uses of final keyword
  • final v/s Immutability
comments powered by Disqus