Can things be avoided from getting Inherited? - The 'final' Keyword
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}
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
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}
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}
1HelperClass.java:7: error: cannot assign a value to final variable a1
2 a1 = a2;
3 ^
41 error
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}
1a1'a value: 5
2a1'a value now: 10
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}
1HelperClass.java:7: error: cannot assign a value to final variable arr1
2 arr1 = arr2;
3 ^
41 error
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}
1Printing array
21 2 3 4 5
3Printing array
41 2 3 10 5
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 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:
I hope this fine distinction between 'final' and 'Immutability' is clear by now.
Takeaways
- Various uses of final keyword
- final v/s Immutability