What is Inheritance?

Imagine you wanted to create a racing game. You would need a bunch of different cars-bugattis, lamborghinis, rolls royce. Each of these cars would have different features; they would have different fuel types, tires, and engines. A car like an Aston Martin would have a spoiler inside the car for aerodynamics. Lambos have cools doors for when you pull up to a race. But all of these cars would also have a lot in common; all cars have gear shifts, gas, breaks, and steering.

We could model each car with its own class, with attributes and methods specific to each car. But we would find that we're repeating a lot of the same code over and over - it doesn't really make sense to redefine the methods for gas and breaks for every single car, when it will do the same thing.

This is a great use case for inheritance. We can define a base "Car Class" that has the methods and attributes common to every car - steering methods, gas and break methods, and attributes like speed and miles per gallon.

Each car will "extend" from this base class. This means that it "inherits" the methods and attributes in the base Car Class (this is why it's called Inheritance). But each of the new car classes, for example a Bugatti Class, will have its own special methods and attributes.

public class Car {
    protected String brandName;
    protected double range;
    protected double doorNumber;
    protected double maxSpeed;
    
    // Constructor for the attributes present in the superclass
    public Car(String brandName, double range, double doorNumber, double maxSpeed) {
        this.brandName = brandName;
        this.range = range;
        this.doorNumber = doorNumber;
        this.maxSpeed = maxSpeed;
    }
    
    public void gas () {
        System.out.println("Go!");
    }
    
    public void brake () {
        System.out.println("Stop!");
    }
    
    public void gearShift () {
        System.out.println("Use the stick");
    }
    
    public void steer () {
        System.out.println("turning left...");
    }
    
    public void horn () {
        System.out.print("honking... ");
    }
}
public class TeslaModelS extends Car {
    // Additional attribute not present in the superclass
    protected String hornSound; 
    
    // Constructor for Subclass
    public TeslaModelS(String brandName, double range, double doorNumber, double maxSpeed, String hornSound) {
        // We use the Superclass constructor for the shared attributes through the keyword "super"
        super(brandName, range, doorNumber, maxSpeed);
        // hornSound is not in the Superclass, so we add it separately in the constructor
        this.hornSound = hornSound;
    }
    
    // We use override to change the functionality in the subclass of an existing method in the superclass
    @Override
    public void gearShift () {
        System.out.println("Use the gear selector next to the wheel");
    }
    public void steer () {
        System.out.println("turning right...");
    }
    
    // Here, we don't fully change the functionality of the existing horn method in the superclass
    // Instead, we take all of the functionality of the superclass method, and then add on to it
    public void horn () {
        super.horn();
        System.out.println(hornSound);
    }
    
    public static void main(String[] args) {
        // 5 argument constructor
        TeslaModelS modelS = new TeslaModelS("Tesla", 396, 4, 200, "eugh");
        // Example of late binding
        Car car = new TeslaModelS("Tesla", 396, 4, 200, "brrr");
        // We can still use the methods from the child class, even though we didn't mention them in the subclass!
        modelS.gas();
        // Using the overridden method
        modelS.gearShift();
        modelS.steer();
        // Using the method we added on to
        modelS.horn();
        car.horn();
    }
    
    
}
TeslaModelS.main(null);
Go!
Use the gear selector next to the wheel
turning right...
honking... eugh
honking... brrr

Overriding Methods

Overriding allows a subclass or child class to provide a specific implementation of a method that has already been provided by a super-classes or parent classes. When a method in a subclass has the same name, same parameters or signature, and same return type (or sub-type) as a method in its super-class, then the method in the subclass will override the method in the super-class.

Vocab

  • Inheritance: define a base class like a car class that has methods and attributes common. For example, there can be a car steering method, gas and break method, and attributes like speed
  • Extend: Each car that is created will extend from this base class thus inheriting the methods and attributes defined in the base class New car classes with have special features
  • Super Class: Base class that has all the generic methods (base Car Class that has generic methods for all cars) protected: access modifier isn't affected by outside
  • Subclass constructor: inherits all methods and attributes from super class In car example, TeslaModelS was a subclass In personal example, IphoneX and IphoneV are subclasses
  • super Keyword: superclass objects - used to call superclass methods and access the constructor use constructors defined in the superclass can add specific attributes super() is the use of the keywords
  • Overloading a method (same name different parameters): two methods with the same name but different arguments; static polymorphism or compile time polymorphism
  • Overriding a method (same signature of a method): subclass has specific implementation of a method that the superclass provides
  • Polymorphism: doing one task in many ways through inheritance gearShift in cars can be used in two different ways in two different classes - two implementations through overriding

Hacks 1-3

public class Phone {
    protected String brandName;
    protected double version;
    protected double cameraNumber;
    protected double maxVolume;
    
    // Constructor for the attributes present in the superclass
    public Phone(String brandName, double version, double cameraNumber, double maxVolume) {
        this.brandName = brandName;
        this.version = version;
        this.cameraNumber = cameraNumber;
        this.maxVolume = maxVolume;
    }
    
    public void flash () {
        System.out.println("On!");
    }
    
    public void homebutton () {
        System.out.println("Yes!");
    }
    
    public void darkmode () {
        System.out.println("on!");
    }
    
    public void powerbutton () {
        System.out.println("turning on...");
    }
    
    public void camera () {
        System.out.print("photo... ");
    }
}
public class IphoneX extends Phone {
    // Additional attribute not present in the superclass
    protected String cameratype; 
    
    // Constructor for Subclass
    public IphoneX(String brandName, double version, double cameraNumber, double maxVolume, String cameratype) {
        // We use the Superclass constructor for the shared attributes through the keyword "super"
        super(brandName, version, cameraNumber, maxVolume);
        // hornSound is not in the Superclass, so we add it separately in the constructor
        this.cameratype = cameratype;
    }
    
    // We use override to change the functionality in the subclass of an existing method in the superclass
    @Override
    public void darkmode () {
        System.out.println("The screen is now dark!");
    }
    public void powerbutton () {
        System.out.println("Iphone is on...");
    }
    
    // Here, we don't fully change the functionality of the existing horn method in the superclass
    // Instead, we take all of the functionality of the superclass method, and then add on to it
    public void camera () {
        super.camera();
        System.out.println(cameratype);
    }
    
    public static void main(String[] args) {
        // 5 argument constructor
        IphoneX modelS = new IphoneX("Tesla", 396, 4, 200, "taken");
        // Example of late binding
        Phone phone = new IphoneX("Tesla", 396, 4, 200, "saved");
        // We can still use the methods from the child class, even though we didn't mention them in the subclass!
        modelS.flash();
        // Using the overridden method
        modelS.darkmode();
        modelS.homebutton();
        modelS.powerbutton();
        // Using the method we added on to
        modelS.camera();
        phone.camera();
    } 
}
IphoneX.main(null);

public class IphoneV extends Phone {
    
    public IphoneV (String brandName, double version, double cameraNumber, double maxVolume) {
        super(brandName, version, cameraNumber, maxVolume);
    }
    
    @Override
    public void darkmode () {
        System.out.println("Dark mode is on!");
    }
    
    public void dimensions (int a) {
        System.out.println("Length is " + a);
    }
    
    public void dimensions (int a, int b) {
        System.out.println("Length is " + a + " and height is " + b);
    }
    
        
    public static void main(String[] args) {
        // 4 superclass argument constructor
        IphoneV phone2 = new IphoneV("Apple", 348, 4, 145);
        // Using the overridden method
        phone2.darkmode();
        // Using the overloaded method
        phone2.dimensions(1);
        phone2.dimensions(1, 1);

    }
}
IphoneV.main(null);
On!
The screen is now dark!
Yes!
Iphone is on...
photo... taken
photo... saved
Dark mode is on!
Length is 1
Length is 1 and height is 1