Java > Object-Oriented Programming (OOP) > Inheritance > Final Keyword (Methods, Classes, Variables)

Final Keyword: Preventing Inheritance and Modification

This snippet demonstrates the use of the final keyword in Java to prevent inheritance, method overriding, and variable modification. Understanding final is crucial for designing robust and maintainable object-oriented systems.

Base Class with Final Method and Final Class

This code illustrates three uses of the final keyword: 1. Final Class: The FinalClass is declared as final. This prevents any other class from inheriting from it. Attempting to create a SubClass extending FinalClass will result in a compile-time error. 2. Final Method: The finalMethod within FinalClass and RegularClass is declared as final. This prevents subclasses from overriding the method. Attempting to override finalMethod in a subclass will result in a compile-time error. 3. Final Variable: The finalVariable within FinalClass is declared as final. This means its value cannot be changed after it's initialized. Similarly, localFinalVariable in main is a final local variable. The commented-out sections show what would cause compilation errors.

final class FinalClass {
    private final int finalVariable;

    public FinalClass(int finalVariable) {
        this.finalVariable = finalVariable;
    }

    public final void finalMethod() {
        System.out.println("This is a final method. Cannot be overridden.");
    }

    public int getFinalVariable() {
        return finalVariable;
    }

    public void display() {
        System.out.println("Final Class Display: " + finalVariable);
    }
}

// This class would cause a compilation error because FinalClass is final
// class SubClass extends FinalClass {} 

class RegularClass {
    public final void finalMethod() {
        System.out.println("This is a final method in RegularClass");
    }
}

class SubRegularClass extends RegularClass {
    //Trying to override finalMethod causes a compilation error
    //    @Override
    //    public void finalMethod() {
    //        System.out.println("Attempting to override final method");
    //    }
}

public class FinalExample {
    public static void main(String[] args) {
        final int localFinalVariable = 10; // Local final variable
        // localFinalVariable = 20; // Error: Cannot assign a value to final variable 'localFinalVariable'

        FinalClass finalObj = new FinalClass(5);
        finalObj.finalMethod();
        System.out.println("Final Variable Value: " + finalObj.getFinalVariable());

        RegularClass regularObj = new RegularClass();
        regularObj.finalMethod();

        SubRegularClass subRegularObj = new SubRegularClass();
        subRegularObj.finalMethod();
    }
}

Concepts Behind the Snippet

The final keyword is a non-access modifier that signifies immutability or prevention of extension. It's a fundamental concept in Java for controlling inheritance and ensuring data integrity. * Final Class: Guarantees that the class's implementation will remain unchanged and prevents unintended behavior through inheritance. * Final Method: Ensures that the method's behavior remains consistent across all subclasses. It also allows the compiler to perform certain optimizations. * Final Variable: Ensures that the variable's value remains constant after initialization. This is crucial for representing constants and immutable data.

Real-Life Use Case

Consider a scenario where you're building a security library. You might have a PasswordHasher class with a hashPassword method. To ensure consistent hashing algorithms across the system and prevent subclasses from altering the hashing process, you would declare both the class and the hashPassword method as final. Similarly, API keys are usually defined as final variables.

final class PasswordHasher {
    private final String salt;

    public PasswordHasher(String salt) {
        this.salt = salt;
    }

    public final String hashPassword(String password) {
        // Hashing logic here, utilizing the final salt
        return password + salt; // Simplified example
    }
}

Best Practices

Here are some best practices when using the final keyword: * Use final for classes that represent immutable concepts or entities. * Use final for methods when you want to guarantee that the method's implementation is not changed by subclasses. * Use final for variables that should represent constants or that should only be assigned once. * Consider the impact on extensibility when making a class or method final. It prevents further modification through inheritance or overriding. * Use meaningful names for final variables, especially if they represent constants, to improve code readability (e.g., MAX_RETRIES instead of just max).

Interview Tip

Be prepared to explain the different uses of the final keyword and the reasons behind each use case. Common interview questions include scenarios where you would use final and the impact of using final on inheritance and method overriding. For example: 'Explain the difference between finally, finalize, and final keywords'. Also, consider potential design implications when declaring a class final versus leaving it open for extension.

When to Use Them

* Final Class: Use when you want to prevent inheritance. This is useful for immutable classes or classes where the implementation is critical and should not be changed. * Final Method: Use when you want to prevent overriding. This is useful when you want to ensure that a method's behavior remains consistent across all subclasses. Use with caution, as it reduces flexibility. * Final Variable: Use when you want to ensure that a variable's value cannot be changed after initialization. This is useful for constants, immutable objects, and values that should only be assigned once.

Memory Footprint

The final keyword itself doesn't directly impact memory footprint at runtime. However, the compiler can perform optimizations when it knows that a variable, method, or class is final. For instance, it might be able to inline final methods, reducing the overhead of method calls. For final variables, the compiler can sometimes replace the variable with its constant value directly, optimizing the code.

Alternatives

Instead of using final to prevent inheritance, consider using composition. Instead of inheriting from a class, you can include an instance of that class as a member variable. This allows you to reuse the functionality of the other class without inheriting its interface. For preventing method overriding, consider making methods private if they are only used within the class itself. Private methods are implicitly final. Note that in modern Java, records provide an alternative for creating immutable data classes more concisely.

Pros

* Increased Security: Prevents unauthorized modification of critical classes and methods. * Improved Performance: Allows the compiler to perform optimizations. * Enhanced Code Readability: Clearly indicates which parts of the code are intended to be immutable or non-extensible. * Guaranteed Consistency: Ensures that certain behaviors remain consistent across all parts of the system.

Cons

* Reduced Flexibility: Limits the ability to extend or modify existing code. Overuse can lead to rigid designs. * Potential for Code Duplication: May require duplicating code if inheritance is prevented and functionality needs to be reused in different contexts. Carefully evaluate the trade-offs between flexibility and immutability.

FAQ

  • What is the difference between final, finally, and finalize?

    final is a keyword used to define entities that cannot be changed (classes, methods, variables). finally is a block of code that is always executed after a try block, regardless of whether an exception was thrown. finalize is a method called by the garbage collector on an object when garbage collection determines that there are no more references to the object.
  • Can a final variable be initialized later?

    A final variable must be initialized exactly once. It can be initialized either at the time of declaration or within the constructor of the class.
  • Can a final method be overloaded?

    Yes, a final method can be overloaded. Overloading refers to defining multiple methods with the same name but different parameters. final only prevents overriding (redefining the method with the same signature in a subclass).