En una revisión de código, seguro que alguna vez has oído: «Añade final a esta clase».
Parece una simple costumbre, pero entender el motivo cambia tu forma de leer el código.
En resumen, final declara: «Esta clase no se ampliará mediante herencia». Es una sola línea, pero aporta rendimiento y estabilidad de diseño.
Hoy veremos por qué se recomienda añadir final a las clases de Swift.
¿Qué es exactamente final?
final es una palabra clave que impide la herencia y la sobrescritura.
Al ponerlo delante de una clase, ninguna otra clase puede heredar de ella.
Delante de un método o una propiedad, también puede impedir la sobrescritura de ese miembro.
final class Logger {
func log(_ msg: String) { print("[LOG] \(msg)") }
}
let logger = Logger()
logger.log("Pago completado") // Imprimir: [LOG] Pago completado
Si intentas heredar de Logger escribiendo class FileLogger: Logger, se produce un error de compilación.
Es como dejar claro: «Esta clase termina aquí».
¿Por qué final mejora el rendimiento?
La primera razón es el rendimiento. La clave está en el método de despacho (dispatch).
Mientras exista la posibilidad de herencia, el compilador no sabe hasta el tiempo de ejecución qué método se invocará y debe buscarlo en una tabla cada vez.
Esto se llama despacho dinámico. Primero consulta la tabla de métodos (vtable) de cada clase y después realiza la llamada.
Con final, en cambio, la situación cambia.
Como ninguna subclase puede intervenir, el compilador puede estar seguro de que «esta llamada siempre corresponde a este método».
Por eso llama directamente, sin consultar la tabla. Es el despacho estático.
Además, los métodos cortos pueden insertarse directamente en el punto de llamada mediante inlining.
El coste de una llamada no es grande, pero si el método se ejecuta miles de veces dentro de un bucle, la diferencia se acumula.
La verdadera razón es el «diseño», más que el rendimiento
De hecho, considero esto más importante que el rendimiento.
Se trata de evitar el problema de la clase base frágil (fragile base class).
Si dejas abierta la herencia, cualquiera puede intervenir libremente en el funcionamiento interno de la clase padre que creaste.
Basta cambiar inadvertidamente un método del padre para que fallen en cadena sus hijos que lo sobrescriben.
La herencia es la relación que más rompe la encapsulación. El hijo acaba viendo incluso las entrañas del padre.
Por eso los principios del diseño orientado a objetos dicen lo siguiente.
Diseña y documenta para la herencia. Si no vas a hacerlo, prohíbela. Es el famoso consejo de Effective Java.
final es la herramienta para aplicar este consejo en el código.
El compilador impone la intención: «No la diseñé para herencia, así que no la dejaré abierta».
Cuando la intención es clara, los compañeros también se pierden menos al leer el código más adelante.
Entonces, ¿hay que añadirlo siempre?
Hay algo importante: en Swift, un tipo por referencia no tiene por qué ser una clase.
Si el estado puede tratarse como un valor, struct suele ser una opción mejor. struct no admite herencia desde el principio.
Antes de preguntar «¿añado final?», conviene preguntar «¿realmente tiene que ser una clase?».
Si aun así necesitas una clase, puedes decidir según estos criterios.
| Situación | Criterio |
|---|---|
| Clase sin herencia prevista | Añadir final (valor predeterminado) |
| Código con llamadas repetidas sensibles al rendimiento | Usar final para favorecer el despacho estático |
| Framework con puntos de extensión claramente diseñados | Dejar la herencia abierta |
| API que presupone herencia, como UIViewController | No añadirlo |
Puedes recordarlo así.
- Si no se pretende herencia, añade final por defecto
- Abre solo los puntos de extensión que quieras ofrecer
- Si puede ser struct, evita usar una clase desde el principio
Así pueden preguntarlo en una entrevista
P. ¿Qué ventajas ofrece añadir la palabra clave final?
Impide la herencia y la sobrescritura, haciendo explícita la intención de diseño. Además, el compilador puede usar despacho estático en lugar de dinámico, reducir el coste de las llamadas y aplicar optimizaciones de inlining.
P. Entonces, ¿hay que añadir final a todas las clases?
Las clases diseñadas y documentadas para herencia deben permanecer abiertas. Sin esa intención, es más seguro cerrarlas por defecto; si su semántica encaja con un valor, conviene considerar primero struct.
Una sola línea con final no es un simple consejo de optimización: responde a «¿cómo se usará esta clase?».
Desde hoy, cada vez que crees una clase, pregúntate: «¿Hay alguna razón para permitir la herencia?». Ese hábito hará tu código mucho más sólido.

