Inheritance Unit 3
Inheritance Unit 3
To inherit a class, you simply incorporate the definition of one class into
another by using the extends keyword.
// Create a superclass.
class A {
int i, j;
void showij() {
System.out.println("i and j: " + i + " " + j);
}
}
// Create a superclass.
class A {
int i; // public by default
private int j; // private to A
void setij(int x, int y) {
i = x;
j = y;
}
}
// A's j is not accessible here.
class B extends A {
int total;
void sum() {
total = i + j; // ERROR, j is not accessible here
}
}
class Access {
public static void main(String args[]) {
B subOb = new B();
subOb.setij(10, 12);
// This program uses inheritance to extend Box.
class Box {
double width;
double height;
double depth;
// construct clone of an object
Box(Box ob) { // pass object to constructor
width = ob.width;
height = ob.height;
depth = ob.depth;
}
Output :
Volume of mybox1 is 3000.0
Weight of mybox1 is 34.3
Volume of mybox2 is 24.0
Weight of mybox2 is 0.076
// Here, Box is extended to include color.
class ColorBox extends Box {
int color; // color of box
ColorBox(double w, double h, double d, int c) {
width = w;
height = h;
depth = d;
color = c;
}
}
A Superclass Variable Can Reference a Subclass Object
A reference variable of a superclass can be assigned a reference to any subclass derived
from that superclass.
class RefDemo {
public static void main(String args[]) {
BoxWeight weightbox = new BoxWeight(3, 5, 7, 8.37);
Box plainbox = new Box();
double vol;
vol = weightbox.volume();
System.out.println("Volume of weightbox is " + vol);
System.out.println("Weight of weightbox is " +weightbox.weight);
System.out.println();
// assign BoxWeight reference to Box reference
plainbox = weightbox;
vol = plainbox.volume(); // OK, volume() defined in Box
System.out.println("Volume of plainbox is " + vol);
// The following statement is invalid because plainbox does not define a weight
member.
// System.out.println("Weight of plainbox is " + plainbox.weight);
}
}
Using super
In the preceding examples, classes derived from Box were not implemented as efficiently
or as robustly as they could have been. For example, the constructor for BoxWeight explicitly
initializes the width, height, and depth fields of Box. Not only does this duplicate code
found in its superclass, which is inefficient, but it implies that a subclass must be granted
access to these members.
However, there will be times when you will want to create a superclass that keeps the
details of its implementation to itself (that is, that keeps its data members
private).
In this case, there would be no way for a subclass to directly access or initialize these
variables on its own.
Since encapsulation is a primary attribute of OOP, it is not surprising that Java provides a
solution to this problem.
Whenever a subclass needs to refer to its immediate superclass, it can do so by use of the
keyword super.
super(arg-list);
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.
// default constructor
Shipment() {
super();
cost = -1;
}
When a class hierarchy is created, in what order are the constructors for the
classes that 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. The following program illustrates when constructors are executed:
// Create a super class.
class A {
A() {
System.out.println("Inside A's constructor.");
}
}
Output :
Inside A's constructor
Inside B's constructor
Inside C's constructor
Method Overriding
In a class hierarchy, when a method in a subclass has the same name and type
signature as a method in its superclass, then the method in the subclass is said to
override the method in the superclass.
When an overridden method is called from within its subclass, it will always refer to
the version of that method defined by the subclass.
// Method overriding.
class A {
int i, j;
A(int a, int b) {
i = a;
j = b;
}
// display i and j
void show() {
System.out.println("i and j: " + i + " " + j);
}
}
class B extends A {
int k;
output:
k: 3
If you substitute this version of A into the previous program, you will see the
following
output:
i and j: 1 2
k: 3
Method overriding occurs only when the names and the type signatures of the two
methods are identical. If they are not, then the two methods are simply
overloaded. For example, consider this modified version of the preceding
example:
output :
Inside A's callme method
Inside B's callme method
Inside C's callme method
Why Overridden Methods?
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.
Part of the key to successfully applying polymorphism is understanding that the
superclasses and subclasses form a hierarchy which moves from lesser to greater
specialization. Used correctly, the superclass provides all elements that a subclass can
use directly. It also defines those methods that the derived class must implement on its
own.
This allows the subclass the flexibility to define its own methods, yet still enforces a
consistent interface. Thus, by combining inheritance with overridden methods, a
superclass can define the general form of the methods that will be used by all of its
subclasses.
Dynamic, run-time polymorphism is one of the most powerful mechanisms that
objectoriented design brings to bear on code reuse and robustness. The ability of
existing code libraries to call methods on instances of new classes without recompiling
while maintaining a clean abstract interface is a profoundly powerful tool.
Applying Method Overriding
output:
Inside Area for Rectangle.
Area is 45
Inside Area for Triangle.
Area is 40
Area for Figure is undefined.
Area is 0
Using Abstract Classes
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. Such a
class determines the nature of the methods that the subclasses must implement. One way
this situation can occur is when a superclass is unable to create a meaningful implementation
for a method.
This is the case with the class Figure used in the preceding example. The definition of area( )
is simply a placeholder. It will not compute and display the area of any type of object.
As you will see as you create your own class libraries, it is not uncommon for a method
to have no meaningful definition in the context of its superclass. You can handle this
situation two ways.
One way, as shown in the previous example, is to simply have it report a warning message.
While this approach can be useful in certain situations—such as debugging—it is not usually
appropriate.
You may have methods that must be overridden by the subclass in order for the subclass to
have any meaning. Consider the class Triangle. It has no meaning if area( ) is not defined. In
this case, you want some way to ensure that a subclass does, indeed, override all necessary
methods. Java’s solution to this problem is the abstract method.
To declare an abstract method, use this
general form:
void callmetoo() {
System.out.println("This is a concrete method.");
}
}
class B extends A {
void callme() {
System.out.println("B's implementation of callme.");
}
}
class AbstractDemo {
public static void main(String args[]) {
B b = new B();
b.callme();
b.callmetoo();
}
}
// Using abstract methods and classes.
abstract class Figure {
double dim1;
double dim2;
Figure(double a, double b) {
dim1 = a;
dim2 = b;
}
// area is now an abstract method
abstract double area();
}
class Rectangle extends Figure {
Rectangle(double a, double b) {
super(a, b);
}
// override area for rectangle
double area() {
System.out.println("Inside Area for Rectangle.");
return dim1 * dim2;
class Triangle extends Figure {
Triangle(double a, double b) {
super(a, b);
}
// override area for right triangle
double area() {
System.out.println("Inside Area for Triangle.");
return dim1 * dim2 / 2;
}
}
class AbstractAreas {
public static void main(String args[]) {
// Figure f = new Figure(10, 10); // illegal now
Rectangle r = new Rectangle(9, 5);
Triangle t = new Triangle(10, 8);
Figure figref; // this is OK, no object is created
figref = r;
System.out.println("Area is " + figref.area());
figref = t;
System.out.println("Area is " + figref.area());
}
}
Using final with Inheritance
While method overriding is one of Java’s most powerful features, there will
be times when you will want to prevent it from occurring. 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.
class A {
final void meth() {
System.out.println("This is a final method.");
}
}
class B extends A {
void meth() { // ERROR! Can't override.
System.out.println("Illegal!");
}
}
Using final to Prevent Inheritance
final class A {
//...
}
// The following class is illegal.
class B extends A { // ERROR! Can't subclass A
//...
}
Local Variable Type Inference and Inheritance
For example, in the past you would declare a local double variable called avg that
is initialized with the value 10.0, as shown here:
In this case, the type is explicitly specified as int and var is the name of the
variable being declared. Even though it is a context-sensitive identifier, there
are a few places in which the use of var is illegal.
It is important to have a clear understanding of how type inference works
within an inheritance hierarchy.
Recall that a superclass reference can refer to a derived class object, and this
feature is part of Java’s support for polymorphism.
However, it is critical to remember that, when using local variable type
inference, the inferred type of a variable is based on the declared type of its
initializer. Therefore, if the initializer is of the superclass type, that will be the
inferred type of the variable. It does not matter if the actual object being
referred to by the initializer is an instance of a derived class. For example,
consider this program:
The Object Class
There is one special class, Object, defined by Java. 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.
Also, since arrays are implemented as classes, a variable of type Object can also
refer to any array.
Object defines the following methods, which means that they are available in
every object.
The methods getClass( ), notify( ), notifyAll( ), and wait( ) are declared as final.
You may override the others. However, notice two methods now: equals( ) and
toString( ).
The equals( ) method compares two objects. It returns true if the objects are equal,
and false otherwise. The precise definition of equality can vary, depending on the type
of objects being compared.
The toString( ) method returns a string that contains a description of the object on
which it is called. Also, this method is automatically called when an object is output
using println( ). Many classes override this method.