Hi! You're already using Java methods and know a lot about them. How method overriding works - 1You've probably faced the situation where one class has many methods with the same name but different parameters. You'll recall that in those cases we used method overloading. Today we're considering another situation. Imagine that we have a single shared method, but it must do different things in different classes. How do we implement this behavior? To understand, let's consider an Animal parent class, which represents animals, and we'll create a speak method in it:
public class Animal {

   public void speak() {

       System.out.println("Hello!");
   }
}
Though we've only just started writing the program, you probably see a potential problem: there are a lot of animals in the world, and they all 'speak' differently: cats meow, ducks quack, and snakes hiss. How method overriding works - 2Our goal is simple: avoid creating loads of speaking methods. Instead of creating a catSpeak() method for meowing, a snakeSpeak() method for hissing, etc., we want to call the speak() method and have the snake hiss, the cat meow, and the dog bark. We can easily achieve this using method overriding. Wikipedia gives the following explanation of the term 'overriding': Method overriding, in object-oriented programming, is a language feature that allows a subclass or child class to provide a specific implementation of a method that is already provided by one of its superclasses or parent classes That is essentially correct. Method overriding lets you take some method of the parent class and write your own implementation in each child class. The new implementation 'replaces' the parent's implementation in the child class. Let's see how this looks in an example. Create 4 classes that inherit our Animal class:
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!");
   }
}
"Here's a small lifehack for the future: to override the parent class's methods, go to the child class's code in IntelliJ IDE, Click Ctrl+O, and choose "Override methods..." in the menu. Get used to using hot keys from the outset — it will help you write programs faster! To specify the behavior we need, we did a few things:
  1. In each child class, we created a method with the same name as the parent class's method.

  2. We told the compiler that naming the method the same as in the parent class wasn't happenstance: we want to override its behavior. To communicate this to the compiler, we set the @Override annotation above the method.
    When placed above a method, the @Override annotation informs the compiler (as well as programmers reading your code): 'Everything is okay. This isn't a mistake. I'm not being forgetful. I'm aware that such a method already exists and I want to override it'.

  3. We wrote the implementation we need for each child class. When the speak() method is called, a snake should hiss, a bear should growl, etc.
Let's see how this works in a program:
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();
   }
}
Console output: Woof! Meow! Growl! Hiss! Excellent! Everything works as it should! We created 4 reference variables that store objects of the Animal parent class, and we assigned instances of 4 different child classes to them. As a result, each object exhibits its own behavior. For each child class, the overridden speak() method replaced the 'native' speak() method in the Animal class (which simply displays 'Hello!'). How method overriding works - 3Overriding has several limitations:
  1. The overridden method must have the same parameters as the parent method.

    If the parent class's speak method has a String parameter, the overridden method in the child class must also have a String parameter. Otherwise, the compiler will generate an error:

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

  2. The overridden method must have the same return type as the parent method.

    Otherwise, we'll get a compiler error:

    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. The access modifier on the overridden method must also be the same as the 'original' method:

    public class Animal {
    
          public void speak() {
    
                System.out.println("Hello!");
          }
    }
    
    public class Cat extends Animal {
    
           @Override
           private void speak() {      // Error!
               System.out.println("Meow!");
           }
    }
Method overriding in Java is one way to implement polymorphism (the principle of OOP that we described in the last lesson). This means that its main advantage is the same flexibility that we discussed previously. We can build a simple and logical system of classes, each having a specific behavior (dogs bark, cats meow), with a common interface — a single speak() method for them all rather than loads of methods, e.g. dogSpeak(), speakCat(), etc.