You have some experience in writing classes at this point. If you notice that certain classes are somewhat related to one another and they share the same data and/or behavior, you can take advantage of inheritance to minimize some of the duplicate code while gaining some functionality. In Java, all classes can inherit attributes (instance variables) and behaviors (methods) from another class. The class being inherited is oftentimes called the parent class, the superclass, or the base class.

The class that is inheriting is called the child or the subclass. Once a class inherits from another, you have defined the is-a relationship which is to say the child class is the same kind of thing as the parent class. For example, a Student is a kind of Person. Orange is a kind of Fruit. iPhone is a SmartPhone.

Below is a diagram showing classes and their relationships with one another. The arrows point to the parent class.

extends Keyword

One of the new keywords in this unit is "extends" as shown below. This keyword is used to declare a class to be a child class of another class.
public class Child extends Parent{ }
public class Car extends Vehicle{}
public class Tesla extends Car{}
As shown above, you now have a slightly different class header. If you check the is-a relationship, it makes logical sense to define the classes as shown: Car is-a Vehicle, Tesla is-a Car.

The other type of relationship a class can have with other classes is "has-a". This just means that a class can contain a reference to other classes. In the example below, the class ShoppingList has-a list of Product objects.
public class ShoppingList{
    //Product is not a child of ShoppingList, but ShoppingList has-a list of Product objects
    private ArrayList list;
}

public and protected Keyword

Any public methods from a parent class will be inherited by the child class. Public instance variables will also be directly accessible, however, anything private will not be and will need to be accessed through getters and setters. Constructors also do not get inherited by child classes. While child classes do not inherit the parent's constructor, they can invoke the parent constructor within the class definition through the user of "super()".  Depending on the number of parameters, type and/or order the call to super will match a constructor in the parent similar to invoking constructors from Unit 5 and Unit 2!

It behaves similar to method overloading where Java knows which one you are trying to use based on the arguments passed to the method. In the example classes shown, the constructor for Student invokes super. Java looks for the constructor in the parent matching the type of parameters passed to super. In the case where the child class must set its own instance variables, the assignment statements and other logic will need to be after the call to super.

public class Person{
    public Person(){ /* implementation not shown */ }         //constructor 1
    public Person(int a){ /* implementation not shown */}   //constructor 2
    public Person(String b){ /* implementation not shown */} //constructor 3
}

public class Student{
    public Student(){
          super(1); //invokes constructor 2 of parent to match argument type!
     }
}

No-Argument-Constructor

By default, Java will add a default (no parameter/argument) constructor if a class does not include one if and only if the class does not have any constructors at all. A call to the parent's default constructor, super() without any arguments, is ALWAYS invoked in a child class if and only if the child class constructors do not explicitly call a non-default super call. By non-default, I mean the no-parameter constructor. This means that your parent class will need to include a default constructor if you plan on this type of functionality. That is if you plan to have constructors in the child class with no explicit calls to a super constructor.

Overloading

Recall that method overloading is when you have multiple methods with the same name in the same class. How does it work with inheritance? What if a child class has a method that is exactly the same as the parent? A child class that has its own sets of methods that already exist in the parent class is overriding these methods. Overriding methods happen anytime a child class has the exact method in its class code as the superclass. This child class method will be invoked instead of the superclass version. This is because child classes that override methods are claiming they provide some sort of functionality that is specific to that child class. Therefore, it is a priority to use the child class methods over the inherited methods from the superclass.

The example shown showcases how the child class method runs over the parent class method. In the Runner, two objects are instantiated but in both cases, the Child class constructor is used to create the objects. When you call the method that is overridden by the Child class, the Child class method runs. In fact, there is no way to downcast these objects as Parent objects to try and force the Parent method to run. This is not the same as calling super from the implementation of the Child class.

public class Runner{
public static void main(String[] arg){
Parent a = new Child();
Parent b = new Child();

a.mys(); //prints "child" - even though a is declared as a Parent class, on runtime, it is a Child-type.
b.mys();//prints "child"
}
}

public class Parent{
public void mys(){ System.out.println("parent"); }
}

public class Child extends Parent{
public void mys(){ System.out.println("child"); }
}

What Should You Inherit?

Any method that is exactly the same between the parent and child should be inherited. In other words, you should leave it only in the parent class and make these methods public. This will allow all classes derived from the parent class to inherit these behaviors (methods). The access to private instance variables that reside in a parent class should be managed through public getters and setters of the parent class. Child classes also do not have access to private instance variables that reside in another class even if they are one of its child classes.

Complete some practice problems dealing with overriding methods!

Invoking Parent Methods in a Child Class


As alluded to in the previous section, outside of the class code there is no way to force a class to call upon its parent class' methods. When you are implementing the child class, writing code that goes in the child class, you can explicitly call upon a parent method through super. You first saw this keyword when writing constructors, but when used to access parent methods, you use the syntax super.method() and NOT super().method().

Below is an example of a child class explicitly calling the parent method. After the parent method runs, it resumes back where it was called and continues running the rest of the code.

public class Parent{
    public void methodA(){}
}    
public class Child{
public void methodA(){
super.methodA();
System.out.println("finished running parent method");
}
}

Application

It is a good idea to invoke parent methods whenever you can reduce repeated code. A trivial example is with the toString method. The parent class can handle creating the String for its instance variables while the children can add to this string to add any additional instance variables it may have added in its implementation. Below is an example of this.

Multiple-level Inheritance

So far we have only covered single-level inheritance. This is when you have a child and a parent class. A parent can have multiple children. In other words, many classes can inherit from another class. They, however, cannot inherit from two classes at the same time. This is known as multiple inheritances and is not supported by the Java language through the traditional class hierarchical system. With that said, let us discuss multiple-level inheritance ( NOT the same as multiple inheritances). The Shapes hierarchy is usually used when discussing inheritance because many will have worked with shapes before so it is easier to grasp. Look at the image below, at the very top is the Shape class. Below are three child classes.

Finally, some of the child classes also have a child. This is what is known as multiple-level inheritance. At the very bottom of the hierarchy, these classes inherit from both their immediate and non-immediate parents! For example, if Shape has a public method mystery then Square objects will inherit this method because their parent class, Rectangle, inherits from Shape.



Polymorphism

Polymorphism is the fancy word used to describe the fact that an object in Java programming always knows its TYPE at run-time. In the previous section, we discussed how to override methods from a parent and when child methods run. What an object decides to run is dependent upon the constructor used to create the object.

Recall the Hierarchical diagram for Shape. If you create an object using the line Shape s = new Square();, the declared type is Shape but the run-time type is Square. Therefore, the object referred to by s behaves like a Square. It will always default to trying to use methods from the Square class before running any methods from the parent class. Of course, its methods can call upon the parent's methods through super. Other examples you have seen are problems dealing with List and ArrayList. For example, you may see something like List myList = new ArrayList(); Although List is an interface, the idea of is-a relationship can still be applied. The compiler checks if ArrayList is-a List to decide whether the statement is valid which it is!

Look through some MC problems to test your ability to apply the idea of Polymorphism.

The Superclass to Rule Them All

The superclass of all classes in Java is the Object class. It is part of the language. Any class that you write will actually automatically inherit from the Object class. Have you ever wondered why it's ok to pass any object you have ever created to print or println? It is because toString is a method from the Object superclass. This is also true with the equals method. Using the toString method for your own class without overriding the toString method results in a nonsensical printout to the console (the hash code).

When using the equals method without overriding, it works only sometimes because it depends on what you are trying to check. Without overriding the equals method, if you compare two objects using == you are actually asking the question "do these two object references reference the same object?" If the answer is yes then it returns true, false otherwise. Takeaways: Override the toString method so you can decide how to represent your object as a String. Override the equals method so you can define what it means for two objects of the class you wrote to be the same.