Object-Oriented Programming: Inheritance and polymorphism are two central pillars of object-orientated programming and refer to some of the ways abstraction is attained.
5-10% of the test
Roughly 2-4 multiple-choice questions
A possible topic of FRQ #4, which could test your ability to create a class, which could extend a parent class.
After learning about classes with the first two principles of object-orientated programming and different data structures, it's time to learn about the last two principles of object-orientated programming: inheritance and polymorphism. Inheritance allows us to have classes that share the properties of another class. Polymorphism includes allowing an object to be called by both its class and the "parent class" as well.
In this unit, you'll learn how to use inheritance and polymorphism to make different classes. Using these, you'll reduce the code you would have to write otherwise with multiple unrelated classes. Once you learn about inheritance and polymorphism, you will know most of what you need to know for real-life object-orientated programming and can code most real-world situations!
A superclass is a general class that serves as a parent or base class for other classes. Subclasses are classes that are derived from a superclass, and they inherit the methods and attributes of their parent class. Inheritance is the ability of a subclass to inherit or acquire the properties of its parent class. It allows subclasses to specialize the behavior of a superclass without having to rewrite the code. This helps reduce code duplication and makes code maintainable.
Inheritance can be used to model real-world relationships between classes. For example, suppose you have an Animal class and a Dog class. The Dog class could inherit from the Animal class and acquire all the properties of the Animal class, such as the ability to move and eat. Moving and eating are properties all animals have, so Animal is the superclass. A Dog class could have methods like bark and wagTail, but not all animals can do that, so the Dog class is the subclass in this relationship.
To create a subclass in Java, you have to use the extends
keyword in the header of the subclass.
public class Dog extends Animal {}
It is important to note that a subclass can only extend one superclass, but a superclass can have many subclasses. The superclass Animal can have subclasses other than Dog, like Cat, Fish, or Cheetah. However, the Dog class cannot extend Animal and Mammal.
By extending Dog from Animal, the Dog class now has methods to move, eat, bark, and wag its tail. The move and eat methods will not have to be written again because the Dog class inherits these methods from the Animal class, making the code less repetitive.
While the methods for subclasses are inherited from the superclass, the constructors are not inherited. To write a constructor for a subclass that uses the constructor of the superclass, we need one important word: super. The super
keyword is used to refer to the constructor of the parent class from within the subclass constructor. This allows the subclass constructor to call the parent class constructor, which can be used to initialize the variables of the parent class. By calling the parent class (superclass) constructor, the subclass constructor can extend the behavior of the parent class without having to rewrite the code.
public class Dog extends Animal {
public Dog (String name, int age) {
super(name, age);
}
}
This constructor for the Dog class takes in two parameters, a name and an age for the dog. The super
call accesses the constructor of the superclass and passes these instance variables to the Animal class constructor. The Dog class cannot access the instance variables of the Animal class, which were declared private, so this constructor is the way the Dog class will use the Animal class's instance variables and initialize them.
The purpose of a subclass is to modify or add to the contents of the superclass, which is where method overriding comes in. A method is overridden when the subclass defines a method with the same method signature as the superclass. When this happens, the computer will run the method that was defined in the subclass for an object of the subclass, rather than the one defined in the superclass, because the subclass methods are usually more specific.
In our Animal class, there is a method called move which tells the Animal objects to walk to their destination. However, a bird is also an animal, but it would prefer to fly instead of walk. We can override the move method in the Bird class to give the bird the functionality to fly instead of walk like this:
public class Animal {
public void move(int distance) {
// all animals will walk to their destination
}
public class Bird extends Animal {
public void move(int distance) {
// the birds will fly to their destination
}
}
This is an arbitrary example, but you can still understand how method overriding works. In the Animal and Bird classes, the move method has the same signature, but since the Bird class is a subclass of Animal, all Bird objects will use the move method that enables birds to fly. However, if an Animal object is instantiated, the the move method that enables animals to walk will be called. This is a key point about inheritance. The subclasses inherit and override methods from the superclass, but the superclass cannot see the methods the subclass is writing or overriding.
You already know that the super keyword can be used to call the constructors of the superclass, but in this section we'll discuss how it can be used to call the methods from the superclass as well. Although the subclass automatically inherits all the public methods of the superclass, sometimes we need to slightly modify those methods.
Suppose we created a superclass called Vehicle and a subclass called Car. The Vehicle class contains a method called accelerate()
, which increases the speed of the vehicle. The Car class contains an override of the accelerate()
method that increases the speed of the car, but also enables additional features such as the car's cruise control system. The Car class's implementation of the accelerate()
method uses the super call to access the implementation in the Vehicle class, and then adds additional code to enable the car's cruise control system.
public class Vehicle {
public void accelerate() {
speed += 10; // increase the speed of the vehicle
}
}
public class Car extends Vehicle {
public void accelerate() {
super.accelerate();
//additional code to enable the cruise control of the car
}
}
In this example, the overridden method in the subclass used the method already written in the superclass. Instead of rewriting the accelerate()
method of the Vehicle class to incorporate its functionality into the Car class, we simply used the super keyword to call the accelerate()
method of the Vehicle class. Now, when the accelerate()
method of the car class is called, the accelerate()
method of the Vehicle class is called first, then the additional code is executed.
An inheritance hierarchy is a set of classes that are connected by a parent-child relationship. Each class inherits the characteristics of its parent, and the parent class is referred to as the superclass. A class can have multiple subclasses, and each subclass can have multiple subclasses of its own. This creates a hierarchical structure in which the characteristics of a class are passed down from its parent to its children. The hierarchy of classes forms the basis of an object-oriented programming language like Java.
Below is an image of a hierarchy for the Person superclass. In Java, the Object class is the highest superclass.
Image courtesy of Packt.
This inheritance hierarchy allows us to create a Student object reference, but assign it to a MathStudent object, like so:
Student bob = new MathStudent();
This statement will compile because a MathStudent is a Student since it is a subclass of student. However, the statement
MathStudent bob = new Student();
will not compile because a Student is not a subclass of MathStudent. You can use the saying "All squares are rectangles, but not all rectangles are squares" to remember this concept.
Polymorphism in computer science is the concept that you can access objects of different types through the same interface, or superclass. The method a computer executes on an object is determined by the run-time type of the object, not the declared type. In this statement:
Student ada = new CSStudent();
Student is the declared type of the object, but the run-time type is CSStudent, so if the CSStudent class has overridden some methods of the Student class, the methods of the CSStudent class will be called. This is an example of how the Student object can take on multiple forms. The Student object could also have a run-time type of MathStudent, in which case the methods in the MathStudent class would be called. This idea of one class taking on many forms is the essense of polymorphism.
The Object class is the superclass to all other classes in Java. It defines the equals()
and toString()
methods all objects inherit. However, some objects override these methods for class-specific implementations.
The Person class from above could override the toString method:
public String toString() {
return name + ", " + age + " years old";
}
If the toString method wasn't overridden by the Person class, it would return a string of letters and numbers that make up the reference to the object in the computer's memory. While that information could be useful to the computer, it is not useful to humans, so the equals()
and toString()
methods are often overridden in classes to improve their readability.