¡Hola! Ya usas métodos Java y sabes mucho sobre ellos. Cómo funciona la anulación de métodos - 1Seguramente te has topado con una clase con muchos métodos que tienen el mismo nombre pero diferentes listas de argumentos. Recordará que en esos casos usamos sobrecarga de métodos. Hoy vamos a echar un vistazo a una situación diferente. Imagine que tenemos un método común, pero debería hacer cosas diferentes según la clase en la que se llame. ¿Cómo implementamos este comportamiento? Para entender esto, tomemos la Animalclase principal, que representa a los animales, y creemos un speakmétodo en ella:

public class Animal {
  
   public void speak() {

       System.out.println("Hello!");
   }
}
Aunque recién comenzamos a escribir nuestro programa, probablemente pueda ver un problema potencial: el mundo está lleno de muchos animales, y todos "hablan" de manera diferente: los gatos maúllan, los patos graznan, las serpientes silban, etc. Nuestro objetivo es simple: Cómo funciona la anulación de métodos - 2nosotros quiero evitar crear un montón de métodos para hablar. En lugar de crear un meow()método para maullar, hiss()silbar, etc., queremos que la serpiente silbe, el gato maúlle y el perro ladre cuando speak()se llame al método. Podemos lograr esto fácilmente usando la anulación del método . Wikipedia explica el término de la siguiente manera: Anulación de método, en la programación orientada a objetos, es una característica del lenguaje que permite que una subclase o clase secundaria proporcione una implementación específica de un método que ya proporciona una de sus superclases o clases principales. Eso es básicamente correcto. La anulación le permite tomar algún método de una clase principal y escribir su propia implementación en cada clase derivada. La nueva implementación en la clase secundaria "reemplaza" a la principal. Veamos cómo se ve esto con un ejemplo. Vamos a crear 4 descendientes de nuestra Animalclase:

public class Bear extends Animal {
   @Override
   public void speak() {
       System.out.println("Growl!");
   }
}
public class Cat extends Animal {

   @Override
   public void speak() {
       System.out.println("Meow!");
   }
}

public class Dog extends Animal {

   @Override
   public void speak() {
       System.out.println("Woof!");
   }
}


public class Snake extends Animal {

   @Override
   public void speak() {
       System.out.println("Hiss!");
   }
}
Aquí hay un pequeño truco para el futuro: para anular los métodos de una clase principal, acceda al código de la clase derivada en IntelliJ IDE , presione Ctrl+O y seleccione Anular métodos... en el menú. Acostúmbrate a usar teclas de acceso rápido desde el principio. ¡Acelerarán la codificación! Para obtener el comportamiento deseado, hicimos algunas cosas:
  1. En cada clase descendiente, creamos un método con el mismo nombre que el método de la clase principal.
  2. Le dijimos al compilador que no solo le estamos dando al método el mismo nombre que en la clase principal, sino que queremos anular su comportamiento. Este "mensaje" al compilador se transmite a través de la anotación @Override .
    La anotación @Override encima de un método le dice al compilador (así como a otros programadores que leen su código), "No se preocupe. Esto no es un error ni un descuido. Soy consciente de que este método ya existe y quiero anularlo". .

  3. Escribimos la implementación que necesitamos para cada clase descendiente. Cuando speak()se llama al método, una serpiente debe silbar, un oso debe gruñir, y así sucesivamente.
Veamos cómo funciona esto en un programa:

public class Main {

   public static void main(String[] args) {

       Animal animal1 = new Dog();
       Animal animal2 = new Cat();
       Animal animal3 = new Bear();
       Animal animal4 = new Snake();
      
       animal1.speak();
       animal2.speak();
       animal3.speak();
       animal4.speak();
   }
}
Salida de la consola: ¡

Woof! 
Meow! 
Growl! 
Hiss!
¡Genial, todo funciona como debería! Creamos 4 variables de referencia cuyo tipo es la Animalclase padre y les asignamos 4 objetos diferentes de las clases descendientes. Como resultado, cada objeto se comporta de manera diferente. Para cada una de las clases derivadas, el speak()método anulado reemplaza el método existente speak()de la Animalclase (que simplemente muestra "Hablando:" en la consola). Cómo funciona la anulación de métodos - 3La anulación de métodos tiene varias limitaciones:
  1. Un método anulado debe tener los mismos argumentos que el método de la clase principal.

    Si el speakmétodo de la clase padre toma a Stringcomo entrada, entonces el método invalidado en la clase descendiente también debe tomar a Stringcomo entrada. De lo contrario, el compilador generará un error:

    
    public class Animal {
    
       public void speak(String s) {
    
           System.out.println("Speaking: " + s);
       }
    }
    
    public class Cat extends Animal {
    
       @Override // Error!
       public void speak() {
           System.out.println("Meow!");
       }
    }
    

  2. El método invalidado debe tener el mismo tipo de valor devuelto que el método de la clase principal.

    De lo contrario, obtendremos un error de compilación:

    
    public class Animal {
    
       public void speak() {
    
           System.out.println("Hello!");
       }
    }
    
    
    public class Cat extends Animal {
    
       @Override
       public String speak() {         // Error!
           System.out.println("Meow!");
           return "Meow!";
       }
    }
    

  3. El modificador de acceso del método anulado tampoco puede diferir del original:

    
    public class Animal {
    
       public void speak() {
    
           System.out.println("Hello!");
       }
    }
    
    public class Cat extends Animal {
    
       @Override
       private void speak() {      // Error!
           System.out.println("Meow!");
       }
    }
    
En Java, la anulación de métodos es una forma de implementar el polimorfismo. Eso quiere decir que su principal ventaja es esa flexibilidad de la que hablábamos antes. Podemos construir una jerarquía de clases simple y lógica, cada una con un comportamiento específico (perros que ladran, gatos que maúllan) pero una interfaz única: un speak()método único para todos en lugar de un montón de métodos diferentes, por ejemplo bark(), meow(), etc.