Final Keyword

Stamping Enduring Marks on Code’s Path with ‘final’

The final keyword can be prefixed to methods and classes. If you prefix a method in the parent class, and the child class inherits the methods from the parent, the final keyword prevents the child class from overriding that method. If the class itself is prefixed with final, then no other class can extend that class.

Recap: Review where we left off in the previous article.

https://github.com/dinocajic/php-youtube-tutorials/tree/master

https://blog.devgenius.io/php-p64-anonymous-classes-4d26c9e7281a

The final keyword isn’t that complicated of a concept. We pretty much covered everything you need to know in the intro paragraph, but let’s go through it with a couple of examples.

The two classes that we’ll focus on are Car and Vehicle. The Vehicle class has a get_make_and_model() method that returns a string with the make and model of the instantiated object.

<?php
class Vehicle {
    //...

    protected function get_make_and_model(): string
    {
        return $this->make . " " . $this->model;
    }
}

As you can see, it currently just displays the make and model, but I want to display the year as well. We could make the modification in our Vehicle class or override it inside our Car class. Let’s work on the later and override it in the Car class. Remember, our Car class inherits from the Vehicle class. That’s why we can override the method.

<?php
require_once('Vehicle.php');
//...

class Car extends Vehicle
{
    //...

    public function get_make_and_model(): string
    {
        return $this->year . " " . $this->make . " " . $this->model;
    }
}

Working with the same example as before, we’ll create a specific type of Car, in this case a Lamborghini.

<?php

// Final Keyword
require_once("Car.php");
require_once("Driver.php");

$dino = new Driver( new Class(1996, "Ferrari", "F355") extends Car {} );
$dino->drive();

$harrison = new Driver( new Class(1999, "Lamborghini", "Diablo SV") extends Car {} );
$harrison->drive();

We then pass that Lamborghini to the Driver class. The Driver class has a drive() method that calls the turnOn() and turnOff() methods from the Car class. It’s these methods that call the get_make_and_model() helper method. Instead of accessing those from the Vehicle class, they’ll now access it from inside their own class since it exists within Car.

<?php
require_once('Vehicle.php');
require_once('Engine.php');
require_once('Transmission.php');

class Car extends Vehicle
{
    //...

    public function turnOn(): string
    {
        if ($this->car_on) {
            return "The " . $this->get_make_and_model() . " is already on.";
        }

        $this->car_on = true;
        return $this->get_make_and_model() . " has been turned on.";
    }

    public function turnOff(): string
    {
        if ($this->car_on) {
            $this->car_on = false;
            return "The " . $this->get_make_and_model() . " has been turned off.";
        }

        return "The " . $this->get_make_and_model() . " is already off.";
    }
    
    //...
}

If you’re having trouble following the logic, re-read the previous articles and you’ll see how we’ve made it to here. Really, what you need to understand is that the get_make_and_model() method now exists within the Car class and the turnOn() and turnOff() methods call it. They don’t access the Vehicle get_make_and_model() method anymore since it was overridden. Running the code will yield the expected results, which is displaying the year, make, and model.

Perfect. So far we haven’t touched the final keyword whatsoever. This has just been a recap. If you want to read more about overriding methods, check out my article on that topic.

https://blog.devgenius.io/php-p56-method-overriding-6ae3784b42c9

What if we never want the get_make_and_model() method to get overridden? That’s pretty simple. Just add the keyword final in front of the visibility modifier of the method.

<?php
class Vehicle {
    //...

    final protected function get_make_and_model(): string
    {
        return $this->make . " " . $this->model;
    }
}

If we run our code again, we get the following error.

It states that we cannot override the final method Vehicle::get_make_and_model(), which is what we expected.

So, what can we do if we really need the year? Well, we either have to modify the method within the Vehicle class itself or create an entirely different method like get_year_make_and_model() inside of our Car class. We’ll also have to modify our other Car methods that call the get_make_and_model() method so that they’re now calling the get_year_make_and_model() method.

<?php
require_once('Vehicle.php');
//...

class Car extends Vehicle
{
    //...

    public function get_year_make_and_model(): string
    {
        return $this->year . " " . $this->make . " " . $this->model;
    }
}

Great. So far we’ve looked at adding the final keyword to the method, but what if we wanted to add the final keyword to the class itself? To illustrate this concept, let’s recreate our Lamborghini class and pass it to the driver. We got rid of it in the previous article since we were focusing on anonymous classes.

For simplicity, it’s just an empty class that extends Car. You can pretend that it has all the Lamborghini features though.

<?php
require_once("Car.php");

class Lamborghini extends Car {

}
<?php
// Final Keyword
require_once("Lamborghini.php");
require_once("Driver.php");

$harrison = new Driver( new Lamborghini(1999, "Lamborghini", "Diablo SV") );
$harrison->drive();

Running the code yields the expected results: turning the Lamborghini on, driving it, and then turning it off.

Now, what if some company decides that they want to make a Lamborghini knockoff? They want to easily inherit everything from the Lamborghini class and release it to the market. Instead of extending Car, they literally extend Lamborghini.

<?php
require_once("Lamborghini.php");

class LamborghiniKnockoff extends Lamborghini {

}
<?php
// Final Keyword
require_once("LamborghiniKnockoff.php");
require_once("Driver.php");

$harrison = new Driver( new LamborghiniKnockoff(1999, "Lamborghini", "Diablo SV") );
$harrison->drive();

And guess what? It works exactly the same as the Lamborghini. The company pulled it off. How can Lamborghini protect itself? By including the final keyword in front of the class name.

<?php
require_once("Car.php");

final class Lamborghini extends Car {

}

This automatically breaks the LamborghiniKnockoff class.

The only thing that the LamborghiniKnockoff class can do now is inherit from the Car class itself and try to recreate the Lamborghini in a much more difficult way.

In conclusion, the final keyword in front of a method means that you can’t override that method and in front of a class means that you can’t extend that class.

Anonymous Classes

CONJURING FUNCTIONALITY MYSTIQUE WITH ANONYMOUS CLASSES

PHP – P64:ANONYMOUS CLASSES

Anonymous classes are classes without a name. If you’ve been following this article series, this shouldn’t be that foreign of a concept to you. Think back on anonymous functions.

 Final Keyword

Stamping Enduring Marks on Code’s Path with ‘final’

PHP – P65:final keyword

The final keyword can be prefixed to methods and classes. If you prefix a method in the parent class, and the child class inherits the methods from the parent, the final keyword prevents the child class from overriding that method.

Object Comparison

PITTING CODE TITANS AGAINST EACH OTHER

PHP – P66:OBJECT COMPARISON

When using the comparison operator, PHP checks to see if two objects have the same attributes and values, and if they’re instances of the same class.

Leave a Reply