Overriding in Inheritance, Variable shadowing and Narrowing and Widening


Overriding in Inheritance

Overriding in Inheritance is used when you use a already defined method from a super class in a sub class, but in a different way than how the method was originally designed in the super class. Overriding allows the user to reuse code by using existing material and modifying it to suit the user's needs better.

The following example demonstrates how ClassB overrides the functionality of ClassA by changing what gets sent out through the printing method:

Example:

public static void main(String[] args) {
    CustomClass obj1 = new CustomClass();
    CustomClass obj2 = new SubclassCustomClass();
    obj1.display();
    obj2.display();
}
 
class CustomClass {
    public void display() {
        System.out.println("CustomClass Display");
    }
}
 
class SubclassCustomClass extends CustomClass {
    public void display() {
        System.out.println("SubclassCustomClass Display");
    }
}

Example:

CustomClass Display
SubclassCustomClass Display

Variable shadowing

Variables are SHADOWED and methods are OVERRIDDEN. Which variable will be used depends on the class that the variable is declared of. Which method will be used depends on the actual class of the object that is referenced by the variable.

class Vehicle {
    public int speedFactor = 5;
    public String speedUp() {
        return "Speed Up: Vehicle";
    }
}
 
class RacingCar extends Vehicle {
    public int speedFactor = 10;
 
    public String speedUp() {
        return "Speed Up: RacingCar";
    }
 
    public void performTest() {
    }
 
    public static void main(String[] args) {
        Vehicle vehicle = new RacingCar();
        System.out.println(vehicle.speedFactor + " " + vehicle.speedUp());
        // will print out 5 Speed Up: RacingCar
    }
}

Narrowing and Widening of object references

Casting an instance of a base class to a subclass as in : b = (B) a; is called narrowing (as you are trying to narrow the base class object to a more specific class object) and needs an explicit type-cast.

Casting an instance of a subclass to a base class as in: A a = b; is called widening and does not need a type-cast.

To illustrate, consider the following class declarations, and test code:

class Transport {
}
 
class Sedan extends Transport {
}
 
class Pickup extends Transport {
}
 
class Bike extends Transport {
}
 
class Example {
    public static void main(String[] args) {
 
        Transport transport = new Sedan();
        Sedan sedan = new Sedan();
 
        transport = sedan; // is valid, no cast needed
        Sedan s = transport; // not valid
        Sedan s = (Sedan) transport; // valid
    }
}

The statement Transport transport = new Sedan(); is a valid Java statement. Every instance of Sedan is also a Transport. Therefore, the assignment is legal without the need for an explicit type-cast.

To prevent this situation, we need to add an explicit type-cast:

Sedan s = (Sedan) transport;

The type-cast tells the compiler that we expect the value of sedan to be a Sedan or a subclass of Sedan. If necessary, compiler will insert code to perform a run-time type check. If the check fails, then a ClassCastException will be thrown when the code is executed.

Note that not all type-casts are valid. For example

String s = (String) sedan; // not valid

The Java compiler knows that an instance that is type compatible with Sedan cannot ever be type compatible with String. The type-cast could never succeed, and the JLS mandates that this gives in a compilation error.


Inheritance and Static Methods

In Java, parent and child class both can have static methods with the same name. But in such cases implementation of static method in child is hiding parent class' implementation, it's not method overriding. For example:

class StaticMethodTest {
 
	// static method and inheritance
	public static void main(String[] args) {
		Parent p = new Child();
		p.staticMethod(); // prints Inside Parent
		((Child) p).staticMethod(); // prints Inside Child
	}
 
	static class Parent {
		public static void staticMethod() {
			System.out.println("Inside Parent");
		}
	}
 
	static class Child extends Parent {
		public static void staticMethod() {
			System.out.println("Inside Child");
		}
	}
}

Static methods are bind to a class not to an instance and this method binding happens at compile time. Since in the first call to staticMethod(), parent class reference p was used, Parent's version of staticMethod() is invoked. In second case, we did cast p into Child class, Child's staticMethod() executed.

Basic Programs