La palabra clave final
Parte de la sección Programación Orientada a Objetos del Journey de PHP de Coddy — lección 24 de 91.
Si bien la herencia y la sobrescritura de métodos proporcionan flexibilidad, a veces es necesario evitar que ciertas clases o métodos sean modificados. La palabra clave final bloquea el código, asegurando que implementaciones específicas no puedan ser alteradas por clases hijas.
Cuando marcas un método como final, ninguna clase hija puede sobrescribirlo:
<?php
class BankAccount {
protected $balance;
public function __construct($balance) {
$this->balance = $balance;
}
final public function getAccountId() {
return "ACC-" . spl_object_id($this);
}
}
class SavingsAccount extends BankAccount {
public function addInterest() {
return $this->balance * 0.05;
}
}
$account = new SavingsAccount(1000);
echo $account->getAccountId();
Salida:
ACC-1El método getAccountId() no puede ser sobrescrito en SavingsAccount o cualquier otra clase hija. Esto protege la funcionalidad crítica que debe permanecer consistente en todos los tipos de cuenta.
También puedes marcar una clase entera como final para evitar cualquier herencia:
<?php
final class SecurityManager {
public function authenticate($user) {
return "Authenticating $user";
}
}
$security = new SecurityManager();
echo $security->authenticate("admin");
Salida:
Authenticating adminNinguna clase puede extender SecurityManager. Esto es útil para clases sensibles a la seguridad o cuando el diseño de la clase está completo y no debería modificarse.
Punto clave: Utiliza final en métodos para evitar la sobrescritura mientras se permite la herencia, o en clases para evitar la herencia por completo. Es una herramienta para proteger código crítico que debe funcionar exactamente como fue diseñado.
Desafío
FácilVamos a construir un sistema de gestión de documentos que demuestre cómo la palabra clave final protege la funcionalidad crítica de ser modificada por las clases hijas.
Crearás tres archivos que trabajan juntos para gestionar diferentes tipos de documentos, asegurando al mismo tiempo que ciertas características de seguridad permanezcan bloqueadas:
Document.php— Define una claseDocumentque sirva como base para todos los documentos. Debe tener una propiedad pública$titley una propiedad protegida$author. El constructor acepta tanto el título como el autor. Incluye un métodofinalllamadogetSignature()que devuelva"Signed by: " . $this->author— esta característica de seguridad nunca debe ser alterada por las clases hijas. También añade un método regulargetContent()que devuelva"Document: " . $this->title.Report.php— Define una claseReportque extienda deDocument. Incluye el archivo Document al principio. Esta clase añade una propiedad$department. El constructor debe aceptar el título, el autor y el departamento — utilizaparent::__construct()para los dos primeros, luego establece el departamento. Sobrescribe el métodogetContent()para que devuelva"Report: " . $this->title . " (" . $this->department . ")". La claseReporthereda el método finalgetSignature()sin poder cambiarlo.main.php— Incluye el archivo Report. Recibirás tres entradas: el título del documento, el nombre del autor y el departamento. Crea un objetoReportcon estos valores. Imprime el resultado degetContent()en la primera línea, luego imprime el resultado degetSignature()en la segunda línea.
Este desafío muestra cómo los métodos final protegen la funcionalidad crítica — la generación de la firma permanece consistente en todos los tipos de documentos, mientras que otros métodos como getContent() pueden ser personalizados por cada clase hija. El Report puede cambiar cómo se muestra el contenido, pero el formato de la firma está bloqueado en la clase padre.
Hoja de referencia
La palabra clave final evita que las clases o los métodos sean modificados por clases hijas.
Métodos Finales: Marque un método como final para evitar que las clases hijas lo sobrescriban:
<?php
class BankAccount {
protected $balance;
final public function getAccountId() {
return "ACC-" . spl_object_id($this);
}
}
class SavingsAccount extends BankAccount {
// Cannot override getAccountId()
}
Clases Finales: Marque una clase completa como final para evitar cualquier herencia:
<?php
final class SecurityManager {
public function authenticate($user) {
return "Authenticating $user";
}
}
// Ninguna clase puede extender SecurityManager
Use final en los métodos para proteger una funcionalidad específica mientras permite la herencia, o en las clases para evitar la herencia por completo. Esto es útil para código sensible a la seguridad o cuando las implementaciones deben permanecer consistentes.
Pruébalo tú mismo
<?php
require_once 'Report.php';
// Leer entrada
$title = trim(fgets(STDIN));
$author = trim(fgets(STDIN));
$department = trim(fgets(STDIN));
// TODO: Crear un objeto Report con los valores de entrada
// TODO: Imprimir el resultado de getContent()
// TODO: Imprimir el resultado de getSignature()
?>Esta lección incluye un breve cuestionario. Empieza la lección para responderlo y registrar tu progreso.
Todas las lecciones de Programación Orientada a Objetos
1Fundamentos de OOP
Archivos externosIntroducción a OOPClases vs ObjetosLa palabra clave $thisMétodosPropiedadesConstructor (__construct)Destructor (__destruct)Resumen - Calculadora simple4Herencia
Herencia básicaLa palabra clave parent::Sobrescritura de métodosLa palabra clave finalClases abstractasResumen - Jerarquía de empleados7Encapsulamiento
Público, Protegido, PrivadoModificadores de acceso a fondoGetters y SettersOcultamiento de informaciónPromoción de constructor (8.0)Resumen - Sistema de registros de estudiantes10Conceptos avanzados de POO
Composición vs. HerenciaInyección de dependenciasClases anónimasEnums (PHP 8.1)Fibers (PHP 8.1)Clonación de objetos a fondoGeneradores e Iteradores13Patrones de diseño Parte 2
Patrón CommandPatrón AdapterPatrón DecoratorPatrón Template MethodPatrón StatePatrón CompositePatrón Repository2Espacios de nombres y Autoloading
Introducción a los espacios de nombresLa palabra clave useEstándar de Autoloading PSR-4Autoloader de ComposerResumen - Proyecto organizado5Interfaces y Contratos
Introducción a las InterfacesImplementación de InterfacesImplementación de Múltiples InterfacesInterfaz vs Clase AbstractaType Hinting con InterfacesResumen - Calculadora de Formas8Métodos mágicos
Introducción a los métodos mágicos__toString y __debugInfo__get, __set, __isset, __unset__call y __callStatic__clone y clonación de objetos__serialize y __unserializeResumen - Colección personalizada3Propiedades de clase
Propiedades de instancia vs. estáticasConstantes en clasesMétodos y propiedades estáticosPropiedades privadas y protegidasPropiedades de solo lectura (PHP 8.1)Resumen - Gestor de cuenta bancaria6Polimorfismo
Sobrescritura de métodos revisadaPolimorfismo mediante interfacesType Hinting y tipos de uniónEnlace estático tardíoResumen - Procesador de pagos9Traits
Introducción a los TraitsUso de múltiples TraitsResolución de conflictos de TraitsMétodos abstractos en TraitsTraits frente a herencia12Patrones de diseño Parte 1
Introducción a los patrones de diseñoPatrón SingletonPatrón FactoryPatrón ObserverPatrón Strategy