Implementation Inheritance: This type of inheritance is achieved by
extending classes ie. (acquiring the features of super classes and adding some features of its own. Inheritance of members is closely tried to their declared accessibility. If the super class member is not inherited by its simple name in the subclass, then
that member is not inherited in the subclass examples of these members are
private members, overridden methods constructors.
When we inherit the super class, the super class is specified using the extends clause in the header of the subclass declaration. The subclass only specifies the new and modified members in its class body. The rest of its declaration is made up of its inherited members. If no extends key words is provided, then the class implicitly inherits from the object class, which is the mother of all classes, whether they are defined or system defined classes.In Implementation inheritance only one class can be inherited by the subclass.
This kind of inheritance is sometimes called single or linear implementation inheritance. The name is appropriate, a the subclass inherits the implementation of its super class member. The inheritance relationship can be described as an inheritance hierarchy classes higher up in the hierarchy are more generalized, classes lower down in the hierarchy are more specialized as they customize the inherited behavior by additional properties and behavior.
In Java, implementation inheritance is achieved by extending classes (i.e., adding new fields and methods) and modifying inherited
members.(See example below)Inheritance of members is closely tied to their declared accessibility. If a superclass member is
accessible by its simple name in the subclass (without the use of any extra syntax like super), then that member is considered inherited.
This means that private, overridden, and constructors of the superclass are not inherited.
The superclass is specified using the extends clause in the header of the subclass declaration. The subclass only specifies the additional new and modified members in its class body. The rest of its declaration is made up of its inherited members. If no extends clause is
specified in the header of a class declaration, then the class implicitly inherits from the java.lang.Object class. This implicit inheritance is
assumed in the declaration of the Light class at (1) in Example below.. Also in Example, the subclass TubeLight at (2) explicitly uses the extends clause and only specifies additional members to what it already inherits from the superclassL ight (which, in turn, inherits from the Object class). Members of the superclass Light that are accessible by their simple names in the subclassT ubeLight, are inherited by the
subclass.
Private members of the superclass are not inherited by the subclass and can only be indirectly accessed. The private field indicator of the
superclass Light is not inherited, but exists in the subclass object and is indirectly accessible by piblic methods.
Using appropriate accessibility modifiers, the superclass can limit which members can be accessed directly and, thereby, inherited by its
subclasses . As shown in Example , the subclass can use the inherited members as if they were declared in its
own class body. This is not the case for members that are declared private in the superclass. Members that have package accessibility in
the superclass are also not inherited by subclasses in other packages, as these members are only accessible by their simple names in
subclasses within the same package as the superclass.
Since constructors are not members of a class, they are not
inherited by a subclass.
Example 6.1 Extending Classes: Inheritance and Accessibility
class Light { // (1)
// Instance fields
int noOfWatts; // wattage
private boolean indicator; // on or off
protected String location; // placement
// Static fields
private static int counter; // no. of Light objects created
// Constructor
Light() {
noOfWatts = 50;
indicator = true;
location = "X";
counter++;
}
// Instance methods
public void switchOn() { indicator = true; }
public void switchOff() { indicator = false; }
public boolean isOn() { return indicator; }
private void printLocation() {
System.out.println("Location: " + location);
}
// Static methods
public static void writeCount() {
System.out.println("Number of lights: " + counter);
}
//...
}
class TubeLight extends Light { // (2) Subclass uses the extends clause.
// Instance fields
private int tubeLength = 54;
private int colorNo = 10;
// Instance methods
public int getTubeLength() { return tubeLength; }
public void printInfo() {
System.out.println("Tube length: " + getTubeLength());
System.out.println("Color number: " + colorNo);
System.out.println("Wattage: " + noOfWatts); // Inherited.
// System.out.println("Indicator: " + indicator); // Not Inherited.
System.out.println("Indicator: " + isOn()); // Inherited.
System.out.println("Location: " + location); // Inherited.
// printLocation(); // Not Inherited.
// System.out.println("Counter: " + counter); // Not Inherited.
writeCount(); // Inherited.
}
// ...
}
public class Utility { // (3)
public static void main(String[] args) {
new TubeLight().printInfo();
}
}
Output from the program:-
Tube length: 54
Color number: 10
Wattage: 50
Indicator: true
Location: X
Number of lights: 1
Concepts related to OOPS and Inheritance
The example in this section illustrates basic OOP and Inheritance concepts.
Figure below shows the inheritance relationship between the class String and its superclass Object. A client that uses a String object is defined
in Example code. During the execution of the main() method, the String object created at (1) is denoted by two references: stringRef of the
subclass String and objRef of the superclass Object.
Illustrating Inheritance
// String class is a subclass of Object class
class Client {
public static void main(String[] args) {
String stringRef = new String("Java"); // (1)
System.out.println("(2): " + stringRef.getClass()); // (2)
System.out.println("(3): " + stringRef.length()); // (3)
Object objRef = stringRef; // (4)
// System.out.println("(5): " + objRef.length()); // (5) Not OK.
System.out.println("(6): " + objRef.equals("Java")); // (6)
System.out.println("(7): " + objRef.getClass()); // (7)
stringRef = (String) objRef; // (8)
System.out.println("(9): " + stringRef.equals("C++")); // (9)
}
}
Output from the program:
(2): class java.lang.String
(3): 4
(6): true
(7): class java.lang.String
(9): false
Inheriting from the super class
The subclass String inherits the method getClass() from the superclass Object. A client of the String class can directly invoke this inherited
method on objects of the String class in the same way as if the method had been defined in the String class itself. In Example , this is
illustrated at (2).
System.out.println("(2): " + stringRef.getClass()); // (2).
Extending the super class
The subclass String defines the method length(), which is not in the superclass Object, thereby extending the superclass. In Example
invocation of this new method on an object of class String is shown at (3).
System.out.println("(3): " + stringRef.length()); // (3)
Upcasting.
A subclass reference can be assigned to a superclass reference because a subclass object can be used where a superclass object can be
used. This is called upcasting, as references are assigned up the inheritance hierarchy . In Example , this is
illustrated at (4), where the value of the subclass reference stringRef is assigned to the superclass reference objRef.
Object objRef = stringRef; // (4). Both references denote the same String object after the assignment. One might be tempted to invoke methods exclusive to the String
subclass via the superclass reference objRef, as illustrated at (5).
System.out.println("(5): " + objRef.length()); // (5) Not OK.
However, this will not work as the compiler does not know what object the reference objRef is denoting. It only knows the class of the
reference. As the declaration of the Object class does not have a method called length(), this invocation of length() at (5) would be flagged
as a compile-time error.
Method overriding
In contrast to the situation at (5), the invocation of the equals() method at (6) using the superclass reference objRef is legal because the
compiler can check that the Object class does define a method named equals.
System.out.println("(6): " + objRef.equals("Java")); // (6)
Note that this method is redefined in the String class with the same signature (i.e., method name and parameters) and the same return
type. This is called method overriding
Polymorphism and Dynamic Method Binding
The invocation of the equals() method at (6), using the superclass reference objRef, does not necessarily invoke the equals() method from
the Object class at runtime. The method invoked is dependent on the type of the actual object denoted by the reference at runtime. The
actual method is determined by dynamic method lookup. The ability of a superclass reference to denote objects of its own class and its
subclasses at runtime is called polymorphism. The example above provides a discussion on how polymorphism and dynamic method lookup can
be employed to achieve code reuse.
Under normal program execution, the reference objRef will refer to an object of the String class at (6), resulting in the equals() method from
the String class being executed, and not the one in theO object class.
The situation at (7), where the getClass() method is invoked using the superclass reference objRef, is allowed at compile time because the
Object class defines a method named getClass.
System.out.println("(7): " + objRef.getClass()); // (7)
In this case, under normal program execution, the reference objRef will refer to an object of the String class at (7). Dynamic method lookup determines which method implementation binds to the method signature getClass(). Since no getClass() method is defined in the String
class, the method getClass() inherited from the Object class is thus executed.
Down casting.
Casting the value of a superclass reference to a subclass type is called downcasting This is illustrated in Example
by assigning references down the inheritance hierarchy, which requires explicit casting. stringRef = (String) objRef; // (8)
System.out.println("(9): " + stringRef.equals("C++")); // (9)
At (8), the source reference objRef is of type Object, which is the superclass of the class of the destination reference stringRef. If the
reference objRef actually denoted an object of class String at runtime, the cast would convert it to the proper subclass type, so that the
assignment to the reference stringRef would be legal at (8). The reference stringRef could then be used to invoke the equals() method on
this String object, as at (9). Not surprisingly, the equals() method from the String class would be executed.
The compiler verifies that an inheritance relationship exists between the source reference type and the reference type specified in the cast.
However, the cast can be invalid at runtime. If, at runtime, the reference objRef denotes an object of class Object or some unrelated
subclass of class Object, then obviously casting the reference value to that of subclass String would be illegal. In such a case, a
ClassCastException would be thrown at runtime.