Menu

Herencia en Java: extends, super y sobrescritura

Cómo una subclase de Java hereda campos y métodos con extends, llama al padre mediante super y sobrescribe comportamiento, además de los errores más comunes.

Esta página incluye editores ejecutables: edita, ejecuta y ve el resultado al instante.

La herencia es reutilización del tipo "es-un"

La herencia permite que una clase nueva se construya sobre otra ya existente. La clase nueva -la subclase- obtiene automáticamente los campos y métodos de la superclase, y luego añade o cambia lo que necesite. Recurres a ella cuando un tipo es una variante más específica de otro: un Dog es un Animal, una SavingsAccount es una BankAccount.

Se escribe con la palabra clave extends. Todo lo que sea public o protected en el padre está disponible en el hijo sin tener que reescribirlo.

Dog nunca declara name ni eat(), y sin embargo tiene ambos. De eso se trata justamente: el comportamiento compartido vive en un solo lugar.

super: llegar al padre

El constructor de una subclase debe asegurarse de que el padre se inicialice primero. Eso se hace con super(...), que llama al constructor del padre y debe ser la primera instrucción del constructor de la subclase. Si lo omites, Java inserta una llamada silenciosa al constructor sin argumentos del padre; y si el padre no tiene ese constructor, el código no compila.

La salida muestra que el constructor del padre se ejecuta antes que el cuerpo del hijo: la construcción fluye de arriba abajo en la jerarquía.

Sobrescribir métodos

Una subclase puede reemplazar un método heredado redefiniéndolo con la misma firma. Esto se llama sobrescritura, y siempre deberías marcarla con @Override. La anotación no es obligatoria, pero hace que el compilador verifique que realmente coincides con un método del padre; detecta errores tipográficos como tostring() en lugar de toString(), que de otro modo crearían silenciosamente un método completamente nuevo.

Aunque el arreglo sea de tipo Animal, cada elemento ejecuta su propio speak(). Java elige el método según el objeto real en tiempo de ejecución, no según el tipo declarado de la variable; esa es la base del polimorfismo.

Llamar a la versión del padre con super.method()

Sobrescribir no tiene por qué descartar el trabajo del padre. Usa super.method() para ejecutar la versión heredada y luego ampliarla:

Sin super., llamar a log dentro de TimestampLogger.log se llamaría a sí mismo y entraría en recursión infinita. super. significa explícitamente "la versión del padre".

Campos heredados y acceso

Una subclase ve los miembros public y protected del padre, pero no los private. Los campos private siguen existiendo en el objeto -los propios métodos del padre pueden tocarlos- pero la subclase no puede referenciarlos directamente. Usa protected cuando quieras que las subclases tengan acceso mientras mantienes un miembro oculto del código no relacionado.

class Base {
    private int secret;      // invisible para las subclases
    protected int shared;    // visible para las subclases
}

class Derived extends Base {
    void demo() {
        shared = 5;          // OK
        // secret = 5;       // error de compilación: private en Base
    }
}

Esta es también la razón por la que el constructor de una subclase a menudo debe llamar a super(...): es la única forma de inicializar el estado privado del padre.

Detener la herencia con final

A veces una clase no debería poder extenderse en absoluto: String es final precisamente por esto. Marcar una clase como final prohíbe crear subclases; marcar un método como final prohíbe sobrescribirlo, aunque todavía permite extender la clase.

final class Constants { }            // no se puede heredar de ella

class Config {
    final void load() { }            // las subclases pueden extender Config
                                     // pero no pueden sobrescribir load()
}

Recurre a final cuando el comportamiento de una clase deba estar garantizado y ser inmutable en todo el programa: es una señal deliberada de "no extender", no la opción por defecto.

Un error común: prefiere la composición cuando no es "es-un"

La herencia resulta tentadora porque reutiliza código, pero acopla estrechamente al hijo con el padre. Si la relación no es un genuino "es-un" -digamos un Car que simplemente necesita un Engine- no hagas Car extends Engine. Un coche tiene un motor, no es un motor. Modela eso con un campo (composición) en su lugar:

class Car {
    private Engine engine = new Engine();   // Car TIENE-UN Engine

    void start() { engine.ignite(); }
}

Usa la herencia solo cuando la subclase sea realmente una forma especializada de la superclase y quieras heredar y sustituir su comportamiento.

Siguiente: Interfaces

La herencia con extends te da un único padre y una implementación compartida. Pero una clase solo puede extender una clase, entonces, ¿cómo das una capacidad común a clases que no están relacionadas? Para eso sirven las interfaces: un contrato que muchas clases pueden implementar, y el tema de la próxima página.

Preguntas frecuentes

¿Qué es la herencia en Java?

La herencia permite que una clase (la subclase) reutilice los campos y métodos de otra clase (la superclase) mediante la palabra clave extends. La subclase obtiene automáticamente los miembros públicos y protegidos del padre y puede añadir nuevos o reemplazar el comportamiento heredado mediante la sobrescritura. Modela una relación "es-un": un Dog es un Animal.

¿Qué hace la palabra clave super en Java?

super hace referencia a la clase padre. super(...) dentro de un constructor llama al constructor del padre (y debe ser la primera instrucción), mientras que super.method() llama a la versión del padre de un método que has sobrescrito. Permite que una subclase se apoye en la lógica del padre en lugar de reemplazarla por completo.

¿Cuál es la diferencia entre sobrescritura y sobrecarga en Java?

La sobrescritura redefine un método heredado en una subclase usando la misma firma, cambiando su comportamiento; márcalo con @Override. La sobrecarga define varios métodos con el mismo nombre pero distintas listas de parámetros en la misma clase. La sobrescritura tiene que ver con la herencia y la resolución en tiempo de ejecución; la sobrecarga son simplemente dos métodos que comparten nombre.

Coddy programming languages illustration

Aprende a programar con Coddy

COMENZAR