skip to content

In PHP, what do the abstract and final modifiers mean on a class and on a method, and why can one member not be both?

level: juniorimportance: must knowfreq 62%

answer

  1. abstract: must be completed
  2. final: must not be changed
  3. Cannot instantiate abstract class
  4. final class constants since 8.1
  5. final private method warns since 8.0

basics

~20 s

An abstract class cannot be instantiated and an abstract method has no body, so a concrete child must supply it. A final class cannot be extended and a final method or class constant cannot be overridden. Combining them is contradictory, so PHP rejects it.

solid answer

~40 s

`abstract` marks something **incomplete**: an `abstract class` cannot be instantiated (`new` throws `Error: Cannot instantiate abstract class ...`), and an `abstract` method declares only a signature, which the first concrete subclass must implement. A class with any abstract method must itself be declared abstract. `final` marks something **closed**: a `final class` cannot be extended, a `final` method cannot be overridden, and since PHP 8.1 a `final` class constant cannot be redeclared by a child. One demands a subclass and the other forbids changing it, so PHP rejects `final abstract` with `Cannot use the final modifier on an abstract class` (or method). Since PHP 8.0 `final private` on a non-constructor method only raises a warning, because private methods are never overridden anyway.

code

php · 31 lines
php
<?php
declare(strict_types=1);

abstract class Notification
{
    final public const MAX_RETRIES = 3;

    abstract protected function channel(): string;

    final public function describe(): string
    {
        return 'Sending via ' . $this->channel()
            . ', up to ' . self::MAX_RETRIES . ' tries';
    }
}

final class EmailNotification extends Notification
{
    protected function channel(): string
    {
        return 'smtp';
    }
}

echo (new EmailNotification())->describe(), "\n";

try {
    new Notification();
} catch (Error $e) {
    echo $e->getMessage(), "\n"; // Cannot instantiate abstract class Notification
}

go deeper

for a junior

Recall the two definitions: abstract means cannot be instantiated or must be implemented, final means cannot be extended or overridden. Know the Cannot instantiate abstract class error.

for a middle

Explain the engine rules: a class with an abstract method must be abstract, abstract methods cannot be private in a class, final works on constants since 8.1, and final private only warns since 8.0.

for a senior

Argue for final-by-default on concrete classes and for abstract bases that fix the algorithm while exposing one abstract step, and explain what that does to the cost of later changes.

for a principal

Weigh closing classes with final against consumers who need extension points, and describe how interfaces plus composition keep a library extensible without exposing its hierarchy.

## Two opposite promises PHP's `abstract` and `final` modifiers both talk to **subclasses**, but they say opposite things: - **`abstract`** says: this is incomplete, a subclass must finish it. - **`final`** says: this is complete, no subclass may change it. The declaration rules for both are checked when the class is declared, so breaking one is a fatal compile error; only instantiating an abstract class is caught at runtime, as a thrown `Error`. ## Abstract classes and methods An **abstract class** is a class that cannot be instantiated on its own. It can still have a constructor, properties, constants and fully implemented methods; it simply cannot be the target of `new`. Trying it throws `Error: Cannot instantiate abstract class Notification`. An **abstract method** declares a signature and no body. The rules the engine enforces: 1. A class that declares an abstract method must itself be declared `abstract`, otherwise: `Class X declares abstract method m() and must therefore be declared abstract`. 2. An abstract method cannot have a body (`Abstract function X::m() cannot contain body`), and a normal method must have one. 3. An abstract method cannot be `private` in a class, because nothing could implement it (`Abstract function X::m() cannot be declared private`). Traits are the one place where a private abstract method is allowed. 4. The first **concrete** (non-abstract) subclass must implement every inherited abstract method with a compatible signature, or the declaration fails with `Class X contains N abstract method(s) and must therefore be declared abstract or implement the remaining methods`. 5. A subclass cannot turn an inherited concrete method back into an abstract one. An abstract class with **no** abstract methods is legal; it just prevents direct instantiation. Since PHP 8.4 an abstract class can also declare abstract properties, which belong to the property-hooks feature. ## Final classes, methods and constants `final` closes three different things: | Target | Since | What it forbids | Error when violated | |---|---|---|---| | `final class` | long-standing | any `extends` of the class | `Class X cannot extend final class Y` | | `final` method | long-standing | overriding the method | `Cannot override final method Y::m()` | | `final` class constant | 8.1 | redeclaring the constant in a child | `X::C cannot override final constant Y::C` | A `final` class does not need its methods marked `final`; that would be redundant. Since PHP 8.1 interface constants are also overridable by implementing classes unless they are declared `final`. ## Why they cannot be combined An abstract member exists to be completed by a subclass; a final member exists to prevent subclasses from touching it. A class that is both could never be instantiated and never be extended. PHP rejects the combination outright: - `final abstract class X {}` fails with `Cannot use the final modifier on an abstract class`. - `final abstract public function m();` fails with `Cannot use the final modifier on an abstract method`. ## The final private warning Before PHP 8.0 `final` on a private method was enforced, which blocked a child from declaring an unrelated method of the same name. Since PHP 8.0 private methods are not subject to inheritance rules, so `final private function helper()` produces a compile **warning**, `Private methods cannot be final as they are never overridden by other classes`, and the modifier has no effect. The one exception is `final private function __construct()`, which is still meaningful. ## Applying both to one hierarchy In a notification base class the two modifiers usually sit side by side: 1. The base `Notification` is `abstract`, because "a notification with no channel" is not something the application should ever create. 2. Its `describe()` or `send()` method is `final`, because the order of steps (build, retry, log) must stay the same for every channel. 3. The one step that differs per channel, `channel()`, is `abstract protected`, so each subclass must supply it and outside code cannot call it. 4. `EmailNotification` and `SmsNotification` are `final`, because nothing is meant to specialise them further. The engine then enforces the design: forgetting `channel()` in a new subclass fails at declaration, and nobody can quietly override the fixed algorithm. ## Using them well - Put the **shared algorithm** in a concrete (possibly `final`) method of an abstract base, and leave the varying step as an `abstract protected` method. Each concrete notification then supplies only its channel. - Mark leaf classes `final` by default; it keeps the public surface small and lets you change internals without breaking unknown subclasses. Opening a class later is easy, closing it is a breaking change. - Use `final` constants when a child redeclaring the value would break the parent's own logic.

  • Can an abstract class have a constructor and concrete methods, and when does its constructor run?
    Yes. An abstract class is a normal class that cannot be the target of `new`. Its constructor runs when a concrete subclass is instantiated, either because the subclass inherits it or because the subclass calls `parent::__construct()`. Concrete methods are inherited like any others, which is what makes an abstract base useful for shared logic.
  • Why do many teams mark every class final unless it is designed for extension?
    A `final` class has no subclasses, so its maintainers can change protected members and internal call order without breaking unknown code. Removing `final` later is backward compatible; adding it later breaks every existing subclass. Tests that need a substitute should depend on an interface rather than subclass a concrete class.

saying these in an interview costs you the question

  • An abstract class cannot contain implemented methods or a constructor
  • A class with an abstract method can stay concrete if a child implements it
  • final on a class constant makes its value immutable, unlike normal constants
  • final private methods still stop a child from reusing the method name
  • Every method in a final class must also be marked final