Inheritance

Inheritance Basics

  • Using inheritance, you can create a general class that defines traits common to a set of related items.  
  • This class can then be inherited by other, more specific classes, each adding those things that are unique to it.  
  • In the terminology of Java, a class that is inherited is called a superclass.  
  • The class that does the inheriting is called a subclass.  
  • Therefore, a subclass is a specialized version of a superclass.  
  • It inherits all of the members defined by the superclass and adds its own, unique elements. 
public class Shape {
	private String name;

	public String getName() {
		return name;
	}

	public void setName(String name) {
		this.name = name;
	}
}
public class Rectangle extends Shape {
	private double width, height;

	public double getWidth() {
		return width;
	}

	public void setWidth(double width) {
		this.width = width;
	}

	public double getHeight() {
		return height;
	}

	public void setHeight(double height) {
		this.height = height;
	}

	public double getArea() {
		return getWidth() + getHeight();
	}
}

Member Access and Inheritance

Although a subclass includes all of the members of its superclass, it cannot access those members of the superclass that have been declared as private. 

public class Rectangle extends Shape {
	private double width, height;

	// other codes here
	
	public void updateName() {
		name = "Rectangle";	// compile error since name is private in Shape
	}
}

This program will not compile because the use of j inside the sum( ) method of B causes an access violation. Since j is declared as private, it is only accessible by other members of its own class. Subclasses have no access to it. 

Constructors and Inheritance

  • Although a subclass inherits all of the methods and variables from superclass, it does not inherit constructors. 
  • Subclasses need to create its own constructor and add a call to the superclass’s constructor. 
public class Shape {
	private String name;

	public Shape(String name) {
		setName(name);
	}

	public String getName() {
		return name;
	}

	public void setName(String name) {
		this.name = name;
	}

	@Override
	public String toString() {
		return "name: + " + getName();
	}
}

public class Rectangle extends Shape {
	private double width, height;

	public Rectangle(double width, double height) {
		this(width, height, "Rectangle");
	}

	public Rectangle(double width, double height, String name) {
		super(name);
		setWidth(width);
		setHeight(height);
	}

	// all other codes here

}

Using super Keyword to Call Superclass’s Constructors

  • A superclass’s constructor is always called in addition to the a subclass’s constructor. 
  • The call super() can take any number of arguments appropriate to the various constructors available in the parent class, but it must be the first statement in the constructor. 
  • Example as above. 

Using super Keyword to Access Superclass’s Members

  • The second form of super acts somewhat like this, except that it always refers to the superclass of the subclass in which it is used.  
  • This usage has the following general form super.member 
  • Here, member can be either a method or an instance variable. 
public class Rectangle extends Shape {
	private double width, height;

	// all other codes here

	@Override
	public String toString() {
		return super.toString() + ", width: " + getWidth() + ", height: " + getHeight();
	}

}
public class InheritanceExample {
	public static void main(String[] args) {
		Shape shape = new Shape("Shape");
		System.out.println(shape);

		Rectangle rect = new Rectangle(4, 6, "TheRectangle");
		System.out.println(rect);
	}
	
	/*
	 * name: Shape
	 * name: TheRectangle, width: 4.0, height: 6.0
	 */
}

Creating a Multilevel Hierarchy

  • Up to this point, we have been using simple class hierarchies that consist of only a superclass and a subclass.  
  • However, you can build hierarchies that contain as many layers of inheritance as you like.  
  • As mentioned, it is perfectly acceptable to use a subclass as a superclass of another. 
  • The Employee class contains three attributes (name, salary, and birthdate), as well as one method (getDetails). 
  • The Manager class inherits all of these members and specifies an additional attribute, department, as well as the getDetails method. 
  • The Director class inherits all of the member of Employee and Manager and specifies a carAllowance attribute and a new method, increaseAllowance. 
  • Similarly, the Engineer and Secretary classes inherit the members of the Employee class and might specify additional members (not shown). 

When are Constructors Executed?

  • make up the hierarchy executed?  
  • For example, given a subclass called B and a superclass called A, is A’s constructor executed before B’s, or vice versa?  
  • The answer is that in a class hierarchy, constructors complete their execution in order of derivation, from superclass to subclass.  
  • Further, since super( ) must be the first statement executed in a subclass’ constructor, this order is the same whether or not super( ) is used.  
  • If super( ) is not used, then the default or parameterless constructor of each superclass will be executed. 
public class CallingConstructorsExample {
	public static void main(String[] args) {
		// create object of class C
		C c = new C(); 
	}
	
	/*
	 * Inside A's constructor.
	 * Inside B's constructor.
	 * Inside C's constructor.
	 */
}

//Create a super class. 
class A {

	A() {
		System.out.println("Inside A's constructor.");
	}

}

// Create a subclass by extending class A.
class B extends A {
	
	B() {
		System.out.println("Inside B's constructor.");
	}

}

//Create another subclass by extending B. 
class C extends B {

	C() {
		System.out.println("Inside C's constructor.");
	}

}

Method Overriding

  • In addition to producing a new class based on an old one by additional features, you can modify existing behaviour of the parent class. 
  • If a method is defined in a subclass so that the name, return type, and argument list match exactly those of a method in the parent class, then the new method is said to override the old one. 
	// getDetails() in Shape (superclass)
	public String getDetails() {
		return "name: " + getName();
	}
	// getDetails() in Rectangle (subclass)
	@Override
	public String getDetails() {
		return super.getDetails() + ", width: " + getWidth() + ", height: " + getHeight();
	}

Overriden Methods Support Polymorphism

  • While the examples in the preceding section demonstrate the mechanics of method overriding, they do not show its power.  
  • Indeed, if there were nothing more to method overriding than a name space convention, then it would be, at best, an interesting curiosity, but of little real value. However, this is not the case.  
  • Method overriding forms the basis for one of Java’s most powerful concepts: dynamic method dispatch. 
  • Dynamic method dispatch is the mechanism by which a call to an overridden method is resolved at run time, rather than compile time.  
  • Dynamic method dispatch is important because this is how Java implements run-time polymorphism. 

Polymorphism

  • An object has only one form (the one that is given to it when constructed). 
  • However, a variable is polymorphic because it can refer to objects of different forms. 
  • Java permits you to refer to an object with a variable that is one of the parent class type. 
		// polymorphism
		Shape s = new Rectangle(2, 2, "polyshape");
		System.out.println(s.getDetails());
  • Which getDetails method will be invoked here? From Shape or Rectangle class? 
  • This is the aspect of polymorphism, which is an important feature of object-oriented languages. The behaviour is not determined by the compile time type of the variable, instead it refers to during runtime. 
  • In the above codes, the getDetails method executed is from the object’s real type, the Rectangle class. 

Why Override Methods?

  • As stated earlier, overridden methods allow Java to support run-time polymorphism. 
  • Polymorphism is essential to object-oriented programming for one reason: it allows a general class to specify methods that will be common to all of its derivatives, while allowing subclasses to define the specific implementation of some or all of those methods. 
  • Overridden methods are another way that Java implements the “one interface, multiple methods” aspect of polymorphism. 

Using Abstract Methods

  • Abstract methods are those defined with abstract keyword at its method signature. 

    abstract type name(parameter-list); 

  • Abstract methods mean there is no implementation provided, that is not method body. 

    abstract void callme(); 

There are situations in which you will want to define a superclass that declares the structure of a given abstraction without providing a complete implementation of every method. That is, sometimes you will want to create a superclass that only defines a generalized form that will be shared by all of its subclasses, leaving it to each subclass to fill in the details. 

Using Abstract Classes

  • Any class that contains one or more abstract methods must also be declared abstract.  
  • To declare a class abstract, you simply use the abstract keyword in front of the class keyword at the beginning of the class declaration. 
  • There can be no objects of an abstract class. That is, an abstract class cannot be directly instantiated with the new operator. 
  • Any subclass of an abstract class must either implement all of the abstract methods in the superclass, or be declared abstract itself. 
public abstract class AbstractShape {
	private String name;
	
	public String getName() {
		return name;
	}

	public void setName(String name) {
		this.name = name;
	}
	
	public abstract double getArea();
}
public class Circle extends AbstractShape {
	private double radius;

	public Circle(double radius) {
		this.radius = radius;
	}

	// circle needs to implement the inherited abstract methods
	@Override
	public double getArea() {
		return Math.PI + Math.pow(radius, 2);
	}
}

Using final Keyword with Inheritance

The keyword final has three uses. First, it can be used to create the equivalent of a named constant. This use was described in the preceding chapter. The other two uses of final apply to inheritance. 

Using final to prevent overriding

  • To disallow a method from being overridden, specify final as a modifier at the start of its declaration.  
  • Methods declared as final cannot be overridden. 
public abstract class AbstractShape {
	private String name;

	public String getName() {
		return name;
	}

	public void setName(String name) {
		this.name = name;
	}

	public abstract double getArea();

	public final void display() {
		System.out.println("name: " + getName() + ", area: " + getArea());
	}
}

The display() method in AbstractShape is final, which means it cannot be overriden in the subclass i.e. Circle.

Using final to prevent inheritance

  • Sometimes you will want to prevent a class from being inherited. To do this, precede the class declaration with final.  
  • Declaring a class as final implicitly declares all of its methods as final, too. 
public final class Circle extends AbstractShape {
	private double radius;

	public Circle(double radius) {
		this.radius = radius;
	}

	// circle needs to implement the inherited abstract methods
	@Override
	public double getArea() {
		return Math.PI + Math.pow(radius, 2);
	}

}

The Object Class

  • All other classes are subclasses of Object. 
  • That is, Object is a superclass of all other classes.  
  • This means that a reference variable of type Object can refer to an object of any other class. 
  • Object defines the following methods, which means that they are available in every object. 
MethodPurpose
Object clone()Creates a new object that is the same as the object being cloned. 
boolean equals(Object object)Determines whether one object is equal to another.
Class<?> getClass()Obtains the class of an object at runtime.
int hashCode()Returns the hash code associated with invoing object.
String toString()Returns a string that describes the object.