0 votes
in JAVA by

Final Classes and Methods

1 Answer

0 votes
by

In nearly all cases, the classes we create can be extended by another developer and customized to fit the needs of that developer (we can extend our own classes), even it was not our intent for our classes to be extended. While this suffices for most cases, there may be times when we do not want a method to be overridden, or more generally, have one of our classes extended. For example, if we create a File class that encapsulates the reading and writing of a file on the file system, we may not want any subclasses to override our read(int bytes) and write(String data) methods (if the logic in these methods is changed, it may cause the file system to become corrupted). In this case, we mark our non-extendable methods as final:

public class File {

    public final String read(int bytes) {

        // Execute the read on the file system

        return "Some read data";

    }

    public final void write(String data) {

        // Execute the write to the file system

    }

}

 

Now, if another class wishes to override either the read or the write methods, a compilation error is thrown: Cannot override the final method from File. Not only have we documented that our methods should not be overridden, but the compiler has also ensured that this intention is enforced at compile time.

Expanding this idea to an entire class, there may be times when we do not want a class we create to be extended. Not only does this make every method of our class non-extendable, but it also ensures that no subtype of our class can ever be created. For example, if we are creating a security framework that consumes a key generator, we may not want any outside developer to extend our key generator and override the generation algorithm (the custom functionality may be cryptographically inferior and compromise the system):

public final class KeyGenerator {

    private final String seed;

    public KeyGenerator(String seed) {

        this.seed = seed;

    }

    public CryptographicKey generate() {

        // ...Do some cryptographic work to generate the key...

    }

}

 

By making our KeyGenerator class final, the compiler will ensure that no class can extend our class and pass itself to our framework as a valid cryptographic key generator. While it may appear to be sufficient to simply mark the generate() method as final, this does not stop a developer from creating a custom key generator and passing it off as a valid generator. Being that our system is security-oriented, it is a good idea to be as distrustful of the outside world as possible (a clever developer might be able to change the generation algorithm by changing the functionality of other methods in the KeyGenerator class if those methods we present).

Although this appears to be a blatant disregard for the Open/Closed Principle (and it is), there is a good reason for doing so. As can be seen in our security example above, there are many times where we do not have the luxury of allowing the outside world to do what it wants with our application and we must be very deliberate in our decision making about inheritance. Writers such as Josh Bolch even go so far as to say that a class should either be deliberately designed to be extended or else it should be explicitly closed for extension (Effective Java). Although he purposely overstated this idea (see Documenting for Inheritance or Disallowing It), he makes a great point: We should be very deliberate about which of our classes should be extended, and which of our methods are open for overriding.

Related questions

0 votes
asked May 29, 2020 in JAVA by anonymous
0 votes
0 votes
asked Apr 21, 2020 in JAVA by Hodge
...