Tino Intro To Java

Last modified: January 23, 2023

Overriding Methods

When writing a subclass, in addition to adding totally new methods, you can also change what inherited methods do. For example, if we wanted the admins to have a special prefix on all their messages, we could override the sendMessage method. To do that, you just need to redeclare the method exactly as it is declared in the superclass. The Admin class would then look like this:

public class Admin extends User {

    public void sendMessage(String message) {
        // This code runs whenever sendMessage is called on an Admin object
    }

    public void ban(User user) {
            sendMessage(user.getName() + ", you have been banned!");
    }
}

When overriding a method, sometimes you will want it to execute original inherited code in addition to some new code. you can always call the superclass version of the method through the super keyword by writing super.method(). For example, in the Admin class, we want to print a prefix that identifies the sender as an admin, but the rest of the output should look the same as the output produced by calling sendMessage on a User. We can accomplish that like this:

public void sendMessage(String message) {
    System.out.print("[Admin] ");
    super.sendMessage(message);
}

Polymorphism

It is important to understand that when you make a subclass, you are defining a more specific type of the more general superclass. If you create a variable that holds an instance of a Vehicle, and the Motorcycle class extends Vehicle, then that variable could store a reference to a Motorcycle because a Motorcycle is a type of Vehicle. This same concept also applies to parameters and return values of a method.

For example, using the User and Admin classes from before:

User joe = new Admin();

creates a variable for storing a reference to a User and initializes it to an instance of Admin. Since Admin is a subclass of User, this is legal.

One caveat though, is that since the variable is only declared to be a User, the compiler will complain if you try to call the ban method on joe, even though joe happens to be an Admin. This is because you are only promising the compiler that joe will be a User, not necessarily an Admin. You can call sendMessage on joe because that method is defined in the User class, so it is guaranteed that any subclass of User will also have that method, even if it doesn't do the same thing.

If you call sendMessage on an Admin, the output will be different than if you called sendMessage on a User, so how will the program know which code to run? The answer is that each object knows exactly what type it is, so it will naturally execute its own version of the method whenever that method is called on it. This language feature is called polymorphism and is another core feature of Object Oriented Programming.

Let's take a look at an example of polymorphism in action. Imagine you wrote this Driver class:

public class Driver {
    public static void main(String[] args) {
        User user1 = getRandomUser("Bob");
        User user2 = getRandomUser("Jenny");
        user1.sendMessage("Hello, my name is " + user1.getName());
        user2.sendMessage("Hello " + user1.getName() + ", my name is " + user2.getName());
    }

        // This method returns a User with the given name,
        // and there is a 50% chance the user will be an Admin
    public static User getRandomUser(String name) {
        // declare a variable that can hold a reference to a User
        User randUser;

                // Math.random() generates a random decimal number from 0 (inclusive) to 1 (exclusive)
        if (Math.random() < 0.5) {
            // initialize randUser to an instance of User
            randUser = new User();
        } else {
            // initialize randUser to an instance of Admin.
                        // This is legal because Admin is a subclass of User
            randUser = new Admin();
        }

        // set the user's name to the name passed in
        randUser.setName(name);

        // return the User or Admin that was created
        return randUser;
    }
}

If you ran the program, the output might look like this:

Bob: Hello, my name is Bob
[Admin] Jenny: Hello Bob, my name is Jenny

or like this:

[Admin] Bob: Hello, my name is Bob
Jenny: Hello Bob, my name is Jenny

or even like this:

[Admin] Bob: Hello, my name is Bob
[Admin] Jenny: Hello Bob, my name is Jenny

or this:

Bob: Hello, my name is Bob
Jenny: Hello Bob, my name is Jenny

Inside the main method, there are no conditional statements that check if user1 or user2 is an Admin. It is able to call sendMessage on user1 and user2 without having to know their exact type because all it has to know is they are both instance of either the User class or a subclass of User. The object itself knows what type it is, so it knows what code to execute when the sendMessage method is called on it.

Dark Mode

Outline