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?
answer
- abstract: must be completed
- final: must not be changed
- Cannot instantiate abstract class
- final class constants since 8.1
- final private method warns since 8.0
basics
~20 sAn 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
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
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.
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.
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.
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