Overriding en Java (Anulación de métodos)

POO

En una jerarquía de clases, cuando un método en una subclase tiene el mismo tipo de retorno y firma que un método en su superclase, se dice que el método en la subclase anula (Overriding) el método en la superclase.

1. Qué es Overriding en Java

En cualquier lenguaje de programación orientado a objetos, Anulación o Overriding es una característica que permite que una subclase o clase secundaria proporcione una implementación específica de un método que ya está provisto por una de sus superclases o clases principales. Cuando un método en una subclase tiene el mismo nombre, los mismos parámetros o firma y el mismo tipo de devolución (o subtipo) que un método en su superclase, se dice que el método de la subclase anula el método en la superclase.

Qué es Overriding en Java
Qué es Overriding en Java

La anulación de método es una de las formas en que Java logra el polimorfismo de tiempo de ejecución. La versión de un método que se ejecuta estará determinada por el objeto que se utiliza para invocarla. Si se utiliza un objeto de una clase padre para invocar el método, entonces se ejecutará la versión en la clase padre, pero si se utiliza un objeto de la subclase para invocar el método, entonces se ejecutará la versión en la clase hija.

En otras palabras, es el tipo de objeto al que se hace referencia (no el tipo de la variable de referencia) el que determina qué versión de un método anulado se ejecutará.

2. Ejemplo de anulación de Método

Cuando se llama a un método anulado dentro de una subclase, siempre se referirá a la versión de ese método definida por la subclase. La versión del método definida por la superclase estará oculta. Considera lo siguiente:

class A {
    int i,j;

    A(int a,int b){
        i=a;
        j=b;
    }

    //Mostrar i, j
    void mostrar(){
        System.out.println("i, j: "+i+", "+j);
    }
}
class B extends A {
    int k;

    B(int a, int b, int c){
        super(a,b);
        k=c;
    }

    //mostrar k - esto oculta el método mostrar() en A
    void mostrar(){
        System.out.println("k: "+k);
    }
}

class Override{
    public static void main(String[] args) {
        B b=new B(1,2,3);

        b.mostrar(); //Esto llama a mostrar() en B
    }
}

Salida:

k: 3

Cuando se invoca mostrar() en un objeto de tipo B, se utiliza la versión mostrar() definida en B. Es decir, la versión mostrar() dentro de B anula la versión declarada en A. Si desea acceder a la versión de la superclase de un método ‘anulado’, puede hacerlo utilizando super.

Por ejemplo, en esta versión de B, se invoca la versión de superclase de mostrar() dentro de la versión de la subclase. Esto permite que se muestren todas las variables de instancia.

class B extends A {
    int k;

    B(int a, int b, int c){
        super(a,b);
        k=c;
    }

    //mostrar k - esto oculta el método mostrar() en A
    void mostrar(){
        super.mostrar();
        System.out.println("k: "+k);
    }
}

class Override{
    public static void main(String[] args) {
        B b=new B(1,2,3);

        b.mostrar(); //Esto llama a mostrar() en B
    }
}

Salida:

i, j: 1, 2
k: 3

Aquí, super.mostrar() llama a la versión superclase de mostrar(). La anulación del método ocurre solo cuando las firmas de los dos métodos son idénticas. Si no lo son, entonces los dos métodos están simplemente sobrecargados. Por ejemplo, considere esta versión modificada del ejemplo anterior:

class A {
    int i,j;

    A(int a,int b){
        i=a;
        j=b;
    }

    //Mostrar i, j
    void mostrar(){
        System.out.println("i, j: "+i+", "+j);
    }
}
class B extends A {
    int k;

    B(int a, int b, int c){
        super(a,b);
        k=c;
    }

    //sobrecarga de mostrar()
    void mostrar(String msg){
        System.out.println(msg+k);
    }
}

class Override{
    public static void main(String[] args) {
        B b=new B(1,2,3);

        b.mostrar("Esto es k: "); //Esto llama a mostrar() en B
        b.mostrar(); //Esto llama a mostrar() en A
    }
}

Salida:

Esto es k: 3
i, j: 1, 2

La versión mostrar() en B toma un parámetro de cadena (String). Esto hace que su firma sea diferente de la de A, que no toma parámetros. Por lo tanto, no se produce anulación/overriding (ocultación de nombre).

3. Anulación de métodos soportan polimorfismo

Si bien los ejemplos de la sección anterior demuestran la mecánica de la anulación de método (method
overriding), no muestran todo su poder. La anulación de métodos forma la base de uno de los conceptos más potentes de Java: el despacho dinámico de métodos (también conocido como polimorfismo).

El envío de métodos dinámicos es el mecanismo por el cual una llamada a un método reemplazado se resuelve en tiempo de ejecución en lugar de tiempo de compilación. El envío de métodos dinámicos es importante porque así es como implementa Java el polimorfismo en tiempo de ejecución.

Comencemos por replantear un principio importante: una variable de referencia de superclase puede referirse a un objeto de subclase. Java usa este hecho para resolver llamadas a métodos anulados en tiempo de ejecución.

Cuando se llama a un método reemplazado a través de una referencia de superclase, Java determina qué versión de ese método ejecutar en función del tipo de objeto al que se hace referencia en el momento en que se produce la llamada. Por lo tanto, esta determinación se realiza en tiempo de ejecución. Cuando se hace referencia a diferentes tipos de objetos, se invocarán diferentes versiones de un método reemplazado.

En otras palabras, es el tipo de objeto al que se hace referencia (no el tipo de la variable de referencia) el que determina qué versión de un método reemplazado se ejecutará. Por lo tanto, si una superclase contiene un método anulado por una subclase, cuando se hace referencia a diferentes tipos de objetos a través de una variable de referencia de superclase, se ejecutan diferentes versiones del método.

//Demostración de Polimorfismo
class Sup{
    void desde(){
        System.out.println("desde() Sup");
    }
}

class Sub1 extends Sup{
    void desde(){
        System.out.println("desde() Sub1");
    }
}

class Sub2 extends Sup{
    void desde(){
        System.out.println("desde() Sub2");
    }
}

class Polimorfismo {

    public static void main(String[] args) {
        Sup superob=new Sup();
        Sub1 subob1=new Sub1();
        Sub2 subob2=new Sub2();

        Sup referencia;

        referencia=superob;
        referencia.desde();

        referencia=subob1;
        referencia.desde();

        referencia=subob2;
        referencia.desde();
    }
}

Salida:

desde() Sup
desde() Sub1
desde() Sub2

Este programa crea una superclase llamada Sup y dos subclases de ella, llamadas Sub1 y Sub2. Sup declara que un método llamado desde() y las subclases lo anulan. Dentro del método main(), se declaran los objetos de tipo Sup, Sub1 y Sub2. Además, se declara una referencia de tipo Sup, llamada referencia.

El programa luego asigna una referencia a cada tipo de objeto para referencia y usa esa referencia para llamar a desde(). Como muestra en el resultado, la versión de desde() ejecutada, está determinada por el tipo de objeto al que se hace referencia en el momento de la llamada, no por el tipo de clase de referencia.

4. ¿Por qué anular los métodos?

Como se indicó anteriormente, los métodos reemplazados permiten a Java soportar el polimorfismo en tiempo de ejecución. El polimorfismo es esencial para la programación orientada a objetos por una razón: permite que una clase general especifique los métodos que serán comunes a todas sus derivadas, al tiempo que permite que las subclases definan la implementación específica de algunos o todos esos métodos.

Los métodos reemplazados son otra forma en que Java implementa el aspecto de “una interfaz, múltiples métodos” del polimorfismo. Parte de la clave para aplicar con éxito el polimorfismo es comprender que las superclases y subclases forman una jerarquía que se mueve desde una especialización menor a una mayor.

Si se usa correctamente, la superclase proporciona todos los elementos que una subclase puede usar directamente. También define los métodos que la clase derivada debe implementar por sí misma. Esto le permite a la subclase la flexibilidad de definir sus propios métodos, y aún aplica una interfaz consistente. Por lo tanto, al combinar la herencia con la anulación de métodos/métodos reemplazados, una superclase puede definir la forma general de los métodos que serán utilizados por todas sus subclases.

Herencia en Java
  • Overriding o Anulación

Sobre el Autor:

Hey hola! Yo soy Alex Walton y tengo el placer de compartir conocimientos hacía ti sobre el tema de Programación en Java, desde cero, Online y Gratis.

Deja una Respuesta

*

Este sitio usa Akismet para reducir el spam. Aprende cómo se procesan los datos de tus comentarios.