Free Java Tutorials >> Table of contents >> Extends Super

1. Single Inheritance

The extends keyword

Inheritance allows one class to include all the fields and methods from another class. For example, here we have class Student 'extending' class Person

class Person {
	int age;
	String name;
}
class Student extends Person {
	double gpa;
}
//now given Student student = new Student();
//both student.age and student.name are valid.

In the above example, Student extends Person, so each student has an int age and String name. We call Student the subclass and Person the superclass.

Inheriting methods

A subclass also inherits all the methods from the superclass. Unless these methods are marked 'private', the subclass can also access the superclasses methods:

class Person {
	int age;
	String name;
	int getAge() {
		return age;
	}
}
class Student extends Person {
	double gpa;
	int getGraduationYear() {
		return 2020 - getAge(); //runs getAge() in Person
	}
}
//now given Student student = new Student();
//and student.age = 10;
//getGraduationYear() returns 2020-10 

Overwriting a method in the subclass

You can re-declare a method with the same signature in the subclass. For example, here we have both Person and Student defining a "getName" method:

class Person {
	int age;
	String name = "Alice"; //defaults to Alice
	int getAge() {
		return age;
	}
	String getName() {
		return name;
	}
}
class Student extends Person {
	double gpa;
	int getGraduationYear() {
		return 2020 - getAge(); //runs getAge() in Person
	}
	String getName() {
		return name+" the student";
	}
}
//Given Person person = new Person();
//and Student student = new Student();
//person.getName() returns "Alice"
//student.getName() returns "Alice the student"

When you overwrite a method, the subclass method is run instead of the superclasses method. However, you can still create objects of the superclass type and run the superclass method (in this case new Person())

Constructors are chained

Instantiating an object of the subclass still runs the constructor of the superclass. Take this example:

class Person {
	int age;
	String name = "Alice"; //defaults to Alice
	Person() {
		System.out.println("You made a person!");
	}
	int getAge() {
		return age;
	}
	String getName() {
		return name;
	}
}
class Student extends Person {
	double gpa;
	Student() {
		System.out.println("You made a student!");
	}
	int getGraduationYear() {
		return 2020 - getAge(); //runs getAge() in Person
	}
	String getName() {
		return name+" the student";
	}
}
//Even if you only run Student student = new Student(), both constructors will run.

In the above example, new Student() runs BOTH the new Person() constructor as well as the new Student() constructor. This is a subclass constructor *must* call the superclass constructor. If you do not explicitly call the superclass constructor, Java will automatically call the superclass constructor with no arguments.

2. this,super,super()

this vs super with fields and methods

When a subclass overwrites a field or method of the superclass, it can become nonobvious whether coder refers to the subfield/superfield or submethod/supermethod. You can use super.fieldname to refer to a field in the superclass. Similarly, super.methodname() calls a method in the superclass. Alternatively, this.fieldname will pick the subclass field even if the superclass has the same named field. Similarly, this.methodname() calls the method in the current subclass. This example calls both in 'compareNames':

class Person {
	String getName() {
		return "A-Person";
	}
}
class Student extends Person {
	String getName() {
		return "A-Student";
	}
	public String compareNames() {
		return super.getName() +" vs " + this.getName();
	}
}
//Now run:
//Student s = new Student();
//System.out.println(s.compareNames());  // <--prints
//   "A-Person vs A-Student"

Explicitely calling the super class constructor

Within a subclass constructor, calling super(optional arguments) invokes the superclass' constructor. Furthermore, calls to the superclass constructor must be the very first line of the subclass constructor. By default, a super() with no arguments is always present unless you replace it with your own.

class Person{
	Person() {
		System.out.println("Creating a person with no age");
	}
	Person(int age) {
		System.out.println("Creating a person with age: "+age);
	}
}
class Student extends Person {
	Student() {
		//by default this has a super(), and that runs Person()
	}
}
class Parent extends Person {
	Parent() {
		super(); //this is the same behavior as Student
	}
}
class Teacher extends Person {
	Teacher() {
		super(42);  //All teachers are of age 42
	}
}
//Try invoking new Student(), new Parent(), and new Teacher()

3. The Object class

Every user defined class is a subclass

If a class does not explicitely extend another class, Java will automatically make it extend the "Object" class. This means that every instance of any class in Java is actually an Object. If you create your own class with no superclass, such as class Student, then it subclasses object. If, however, you extend another class such as Person, where Person extends Object, then Student is also an object.

The toString() method

Although the Java Object class has many methods, the most often used is the String toString() method. This method is used by System.out.println to convert an Object into a String. In other words, overwriting this method changes what gets printed when you print your class:

class Thing {  //no need to write extends Object
	public String toString() { //public since Object.toString is public
		//Overwrites Object.toString()
		return "Something Amazing";
	}
}
//Upon creating Thing x = new Thing(),
//System.out.println(x); // prints "Something Amazing";

4. Polymorphism

Reference types must be superclass of object types

Consider the following valid program:

class Person {}
class Student extends Person {}
public class MyProgram {
	public static void main(String[] args) {
		Person x = new Student();
	}
}

Person x = new Student() is valid, even though the left hand type and right hand type are not equal. We call the left hand type the reference type, and the right hand type the object type. The left hand type refers to what type "x" is. The right hand type refers to the new Person() in memory that x points to ("references").

Any reference can point to a subclass of the reference type, so it is valid for x to point to student. Note that it is not valid for a Student reference to point to a new Person(). This is because not every Student "is a" Person.

1st Rule of Polymorphism: Behavior depends on Object (Right)

Polymorphism tell us the behavior of code when the left and right hand types are not equivalent. In Java (as well as most other languages), the behavior of code depends on the object that a reference is pointing to. Here is an example:

class Animal {
	void makeSound() {
		System.out.println("Grrr");
	}
}
class Cat extends Animal {
	void makeSound() {
		System.out.println("Meow");
	}
}
public class MyProgram {
	public static void main(String[] args) {
		Animal a = new Animal();
		a.makeSound(); //Grr, as expected
		Cat c = new Cat(); 
		c.makeSound(); //Meow, as expected

		Animal x = new Cat();  
		x.makeSound(); //Meow, due to polymorphism
		x = new Animal();
		x.makeSound(); //Grr, since x points to a new Animal()
	}
}

2nd Rule of Polymorphism: Compilation depends on the reference (left)

Consider the cat and dog classes below:

class Animal {}
class Cat extends Animal {
	void makeSound() {
		System.out.println("Meow");
	}
}
class Dog extends Animal {
	void makeSound() {
		System.out.println("Woof");
	}
}
public class MyProgram {
	public static void main(String[] args) {
		Animal c = new Cat();
		Animal d = new Dog();
		

		// c.makeSound(); // <-- try uncommenting
		// d.makeSound(); // <-- try uncommenting
		Cat catReference = (Cat)c;
		Dog dogReference = (Dog)d;
		catReference.makeSound();  //Ok
		dogReference.makeSound();  //Ok
	}
}

The two commented lines, c.makeSound and d.makeSound(), do not compile. Java will not even allow you to run the code, since the Animal class does not have a makeSound method. Even though c and d are actually a Cat and a Dog, and even though those objects have a makeSound method, compilation depends on the reference type.

Polymorphic use of Collections

Given multiple objects that share a supertype, it is possible to put them in one Collection that is generic on the supertype. Note that this is true in general: any method that takes a type automatically accepts objects that are subtypes.

import java.util.*;
class Animal {
	void makeSound() { System.out.println("Grrr"); }
}
class Cat extends Animal {
	void makeSound() { System.out.println("Meow"); }
}
class Dog extends Animal {
	void makeSound() { System.out.println("Woof"); }
}
public class MyProgram {
	public static void main(String[] args) {
		ArrayList<Animal> x = new ArrayList<Animal>();
		x.add(new Cat()); //valid
		x.add(new Dog()); //valid
		for(Animal a : x) a.makeSound(); //first meow then woof
	}
}

Previous: Io

Next: Abstract