CodeGym /Cursos /JAVA 25 SELF /Sobrescritura de métodos (override), anotación @Override

Sobrescritura de métodos (override), anotación @Override

JAVA 25 SELF
Nivel 17 , Lección 1
Disponible

1. Sobrescritura de un método

En la vida real hay situaciones en las que el «heredero» se comporta de manera particular. Por ejemplo, todos los animales pueden emitir sonidos, pero el gato — «miau», el perro — «guau», y el programador — «¡ay, otra vez un bug!». En programación, esto se implementa mediante la sobrescritura de un método (override).

La sobrescritura de un método ocurre cuando una subclase proporciona su propia implementación de un método que ya está declarado en la clase padre. Es decir, «sustituye» el comportamiento estándar por uno propio, más específico.

Analogía. Si imaginamos la clase padre como una receta clásica de paella, la sobrescritura del método es cuando la abuela añade su ingrediente secreto. El paella sigue siendo paella, pero el sabor de cada uno es distinto.

Para sobrescribir un método, en la clase hija hay que declarar un método con exactamente la misma firma (nombre, parámetros, tipo de retorno) que en el padre.

Ejemplo: los animales y sus sonidos

class Animal {
    void makeSound() {
        System.out.println("Some generic animal sound");
    }
}

class Dog extends Animal {
    // Sobrescribimos el método makeSound()
    void makeSound() {
        System.out.println("Woof!");
    }
}

class Cat extends Animal {
    // Sobrescribimos el método makeSound()
    void makeSound() {
        System.out.println("Meow!");
    }
}

Ahora, si creas un objeto Dog y llamas a makeSound(), verás "Woof!", y no "Some generic animal sound".

Demostración en código

public class Main {
    public static void main(String[] args) {
        Animal generic = new Animal();
        Dog dog = new Dog();
        Cat cat = new Cat();

        generic.makeSound(); // Some generic animal sound
        dog.makeSound();     // Woof!
        cat.makeSound();     // Meow!
    }
}

Importante: si la subclase no tiene un método con la misma firma, se usará el método del padre.

2. Anotación @Override: para qué sirve y cómo usarla

En Java es habitual marcar los métodos sobrescritos con la anotación @Override. No es un mero adorno del código, sino una herramienta útil:

  • El compilador comprueba que realmente estés sobrescribiendo un método del padre. Si te equivocas en el nombre, en el tipo de parámetro o en el tipo de retorno, el compilador generará un error.
  • Mejora la legibilidad del código. Otro programador ve enseguida: «Ah, este método sobrescribe al del padre».

Ejemplo con @Override

class Dog extends Animal {
    @Override
    void makeSound() {
        System.out.println("Woof!");
    }
}

Si por error escribes void makeSond() (¡errata!) en un método marcado con @Override, el compilador se quejará: "Method does not override or implement a method from a supertype".

Estándares modernos. Usar @Override es una buena práctica y un estándar de la industria. Aunque el compilador no lo exija, pon siempre esta anotación: te facilitará la vida a ti y a tus compañeros.

3. Cómo funciona la llamada a un método sobrescrito

Cuando llamas a un método en un objeto de una subclase, se utilizará la implementación de la subclase, incluso si la variable está declarada como el tipo del padre.

Ejemplo: el polimorfismo en acción

Animal animal = new Dog();
animal.makeSound(); // "Woof!", no "Some generic animal sound"

Aquí la variable es de tipo Animal, pero en realidad contiene un objeto Dog. Java «entiende» que debe invocar el método sobrescrito de Dog. Esto es el polimorfismo (más detalles en próximas lecciones).

4. Restricciones y reglas de la sobrescritura

Firma del método

  • El nombre, el tipo y el orden de los parámetros deben coincidir con el método del padre.
  • El tipo de retorno debe coincidir o ser covariante (un subtipo del tipo de retorno del padre). Por ejemplo, si el padre devuelve Animal y la hija devuelve Dog, está permitido.

Modificadores de acceso

  • No se puede hacer el acceso más estricto que en el padre.
  • Si el método del padre es public, el sobrescrito también debe ser public.
  • Si el del padre es protected, el sobrescrito puede ser protected o public.

Si intentas hacerlo al revés, el compilador dirá: "Cannot reduce the visibility of the inherited method".

Excepciones

  • El método sobrescrito no puede lanzar una nueva excepción comprobada (checked) que no esté en la declaración del padre.
  • Se pueden lanzar menos excepciones que el padre, o subtipos de las mismas.

static, final, private

  • No se pueden sobrescribir métodos declarados como static o final, ni los privados (private).
  • static — es ocultación (hiding), no sobrescritura.
  • final — no se puede sobrescribir en absoluto; Java protege esos métodos.
  • private — no es visible en la clase hija; no se puede sobrescribir (solo puedes declarar un método nuevo con el mismo nombre).

Constructores

Los constructores no se heredan ni se sobrescriben. Cada clase tiene sus propios constructores.

5. Desarrollamos la aplicación didáctica «Zoológico»

¡Hora de aplicar la teoría en la práctica! Sigamos desarrollando nuestra aplicación del «zoológico».

Paso 1. Clase base Animal

public class Animal {
    public void makeSound() {
        System.out.println("Some generic animal sound");
    }

    public void sleep() {
        System.out.println("Zzz...");
    }
}

Paso 2. Subclases Dog y Cat

public class Dog extends Animal {
    @Override
    public void makeSound() {
        System.out.println("Woof!");
    }

    // Método adicional solo para Dog
    public void fetch() {
        System.out.println("Dog brings the stick!");
    }
}

public class Cat extends Animal {
    @Override
    public void makeSound() {
        System.out.println("Meow!");
    }

    // Método adicional solo para Cat
    public void scratch() {
        System.out.println("Cat scratches the sofa!");
    }
}

Paso 3. Usamos la sobrescritura

public class ZooTest {
    public static void main(String[] args) {
        Animal generic = new Animal();
        Animal dog = new Dog();
        Animal cat = new Cat();

        generic.makeSound(); // Some generic animal sound
        dog.makeSound();     // Woof!
        cat.makeSound();     // Meow!

        // ¡Error! La variable de tipo Animal no conoce fetch()
        // Análogamente

        // Pero si indicamos explícitamente el tipo:
        if (dog instanceof Dog) {
            ((Dog) dog).fetch(); // Dog brings the stick!
        }
        if (cat instanceof Cat) {
            ((Cat) cat).scratch(); // Cat scratches the sofa!
        }
    }
}

Comentario:
El método makeSound() funciona de manera polimórfica — se invoca la versión de la clase real del objeto. En cambio, los métodos específicos (fetch, scratch) solo están disponibles mediante una conversión explícita de tipo; esto es importante para entender cómo funcionan la herencia y la sobrescritura.

6. Ejemplo con tipo de retorno (covarianza)

A veces queremos que el método sobrescrito devuelva un tipo más «estrecho». Por ejemplo:

class Animal {
    Animal getFriend() {
        return new Animal();
    }
}

class Dog extends Animal {
    @Override
    Dog getFriend() { // Tipo de retorno — Dog, subtipo de Animal
        return new Dog();
    }
}

Esto se llama covarianza del tipo de retorno y está permitido en Java (desde Java 5).

7. ¿Qué pasa si no usamos @Override?

Si te equivocas por accidente en el nombre del método o en los parámetros, Java no se quejará si no hay anotación @Override. Como resultado, no sobrescribirás, sino que crearás un método nuevo y el comportamiento esperado no cambiará.

Ejemplo de error

class Dog extends Animal {
    // Errata: makeSoud en lugar de makeSound
    void makeSoud() {
        System.out.println("Woof!");
    }
}

public class Main {
    public static void main(String[] args) {
        Animal dog = new Dog();
        dog.makeSound(); // Imprimirá "Some generic animal sound"
    }
}

Si hubiéramos usado @Override, el compilador habría dado un error: "Method does not override or implement a method from a supertype".

8. Errores típicos al sobrescribir métodos

Error n.º 1: ausencia de la anotación @Override.
Sin ella es fácil equivocarse en el nombre del método o en los parámetros. Como resultado, el método no se sobrescribirá y el programa no se comportará como esperabas.

Error n.º 2: intento de restringir el modificador de acceso.
Si el método del padre es public y escribes protected o private, obtendrás un error de compilación.

Error n.º 3: la firma no coincide.
Si los parámetros difieren aunque sea en el tipo, ya no es sobrescritura, sino sobrecarga (overloading).

Error n.º 4: intentar sobrescribir un método final o static.
Java no lo permite: final protege contra la sobrescritura, y los métodos static no se sobrescriben (solo se ocultan).

Error n.º 5: cambiar el tipo de retorno por uno incompatible.
Solo puedes devolver un subtipo del tipo de retorno del padre (covarianza), no un tipo totalmente distinto.

Comentarios
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION