Understanding Java Inheritance Basics
Understanding Java Inheritance Basics
Inheritance
Inheritance is one of the cornerstones of object-oriented programming because it
allows the creation of hierarchical classifications.
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 instance variables and
methods defined by the superclass and adds its own elements.
Inheritance Basics
To inherit a class, you simply incorporate the definition of one class into another by
using the extends keyword.
The following program creates a superclass called A and a subclass called B.
Notice how the keyword extends is used to create a subclass of A.
Example class SimpleInheritance
{
// A simple example of inheritance. public static void main(String args[])
// Create a superclass. {
class A A superOb = new A();
{ B subOb = new B();
int i, j; // The superclass may be used by itself.
void showij( ) superOb.i = 10;
superOb.j = 20;
{
[Link]("Contents of superOb: ");
[Link]("i and j: " + i + " " + j); [Link]();
} [Link]();
} /* The subclass has access to all public members of
// Create a subclass by extending class A. its superclass. */
class B extends A subOb.i = 7;
{ subOb.j = 8;
int k; subOb.k = 9;
[Link]("Contents of subOb: ");
void showk( ) {
[Link]();
[Link]("k: " + k); [Link]();
} [Link]();
void sum() { [Link]("Sum of i, j and k in subOb:");
[Link]("i+j+k: " + (i+j+k)); [Link]();
} }
} }
Contd….
As you can see, the subclass B includes all of the members of its superclass, A.
This is why subOb can access i and j and call showij( ). Also, inside sum( ), i and
j can be referred to directly, as if they were part of B.
Even though A is a superclass for B, it is also a completely independent, stand-
alone class. Being a superclass for a subclass does not mean that the superclass
cannot be used by itself.
The general form of a class declaration that inherits a superclass is shown here:
class subclass-name extends superclass-name
{
// body of class
}
Java does not support the inheritance of multiple superclasses into a single subclass.
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.
EXAMPLE
// Create a superclass.
class A {
int i; // public by default class Access {
private int j; // private to A
public static void main(String
void setij(int x, int y)
{
args[ ])
i = x; {
j = y; B subOb = new B();
} [Link](10, 12);
} [Link]();
// A's j is not accessible here. [Link]("Total is "
class B extends A {
+[Link]);
int total;
void sum() {
}
total = i + j; // ERROR, j is not accessible }
here
}
}
plainbox = weightbox;
vol = [Link]( ); // OK, volume() defined in Box
[Link]("Volume of plainbox is " + vol);
/* The following statement is invalid because plainbox does not define a weight member. */
// [Link]("Weight of plainbox is " + [Link]);
}
}
Contd…
Here, weightbox is a reference to BoxWeight objects, and plainbox is a
reference to Box objects.
Since BoxWeight is a subclass of Box, it is permissible to assign plainbox a
reference to the weightbox object.
It is important to understand that it is the type of the reference variable—not the
type of the object that it refers to—that determines what members can be
accessed.
When a reference to a subclass object is assigned to a superclass reference
variable, you will have access only to those parts of the object defined by the
superclass.
plainbox can’t access weight even when it refers to a BoxWeight object. If
you think about it, this makes sense, because the superclass has no
knowledge of what a subclass adds to it.
It is not possible for a Box reference to access the weight field, because Box
does not define one.
Using super
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.
A subclass can’t directly access or initialize these variables on its own.
Whenever a subclass needs to refer to its immediate superclass, it can do so
by use of the keyword super.
super has two general forms.
1) The first calls the superclass constructor.
2) Used to access a member of the superclass that has been hidden by a member
of a subclass.
Using super to Call Superclass Constructors
A subclass can call a constructor defined by its superclass by use of the
following form of super:
super(arg-list);
Here, arg-list specifies any arguments needed by the constructor in the
superclass. super( )must always be the first statement executed inside a subclass’
constructor.
To see how super( ) is used, consider this improved version of the
BoxWeight( ) class: // BoxWeight now uses super to initialize its Box
attributes.
class BoxWeight extends Box
{
double weight; // weight of box
// initialize width, height, and depth using super( )
BoxWeight(double w, double h, double d, double
m)
{
super(w, h, d); // call superclass constructor
weight = m;
}
}
Contd…
BoxWeight( ) calls super( ) with the arguments w, h, and d. This causes the Box(
) constructor to be called, which initializes width, height, and depth using these
values.
BoxWeight no longer initializes these values itself. It only needs to initialize the
value unique to it: weight. This leaves Box free to make these values private if
desired.
In the preceding example, super( ) was called with three arguments.
Since constructors can be overloaded, super( ) can be called using any form
defined by the superclass.
The constructor executed will be the one that matches the arguments.
EXAMPLE
Pay special attention to this constructor in
BoxWeight( ):
// construct clone of an object
BoxWeight(BoxWeight ob) { // pass object to
constructor
super(ob);
weight = [Link];
}
Contd…
Notice that super( ) is passed an object of type BoxWeight—not of type Box.
This still invokes the constructor Box(Box ob).
As mentioned earlier, a superclass variable can be used to reference any object
derived from that class.
Thus, we are able to pass a BoxWeight object to the Box constructor. Of
course, Box only has knowledge of its own members.
Let’s review the key concepts behind super( ). When a subclass calls super( ), it
is calling the constructor of its immediate superclass.
Thus, super( ) always refers to the superclass immediately above the calling
class. This is true even in a multileveled hierarchy.
A Second Use for super
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:
[Link]
Contd…
Here, member can be either a method or an instance variable.
This second form of super is most applicable to situations in which member
names of a subclass hide members by the same name in the superclass.
// Using super to overcome name
hiding.
class A {
int i;
} class UseSuper {
// Create a subclass by extending public static void main(String
class A. args[ ]) {
class B extends A { B subOb = new B(1, 2);
int i; // this i hides the i in A [Link]();
B(int a, int b) { }
super.i = a; // i in A }
i = b; // i in B
}
void show() {
[Link]("i in superclass: "
+ super.i);
[Link]("i in subclass: " +
i);
}
Creating a Multilevel Hierarchy
We 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.
For example, given three classes called X, Y, and Z, Z can be a subclass of Y,
which is a subclass of x.
In this case, Z inherits all aspects of Y and X. To see how a multilevel
hierarchy can be useful, consider the following program.
In it, the subclass BoxWeight is used as a superclass to create the subclass
called Shipment. Shipment inherits all of the traits of BoxWeight and Box,
and adds a field called cost, which holds the cost of shipping such a parcel.
EXAMPLE
Because of inheritance, Shipment can make use of the previously defined
classes of Box and BoxWeight, adding only the extra information it needs
for its own, specific application.
This is part of the value of inheritance; it allows the reuse of code.
Multilevel Inheritance
16
When Constructors Are Called
When a class hierarchy is created, in what order are the constructors for the
classes that make up the hierarchy called?
For example, given a subclass called Y and a superclass called X, is X’s
constructor called before Y’s, or vice versa?
In a class hierarchy, constructors are called in order of derivation, from
superclass to subclass.
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.
Constructor order Execution
21
Contd…
This example illustrates one other important point: super( ) always refers to
the constructor in the closest superclass.
The super( ) in Shipment calls the constructor in BoxWeight. The super( )
in BoxWeight calls the constructor in Box.
In a class hierarchy, if a superclass constructor requires parameters, then all
subclasses must pass those parameters “up the line.” This is true whether or
not a subclass needs parameters of its own.
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 a subclass, it will always refer to
the version of that method defined by the subclass.
class B extends A {
int k;
B(int a, int b, int c) {
// Method overriding. super(a, b);
class A { k = c;
int i, j; }
A(int a, int b) { // display k – this overrides show() in
i = a; A
j = b; void show() {
} [Link]("k: " + k);
}
// display i and j
}
void show() { class Override {
[Link]("i and j: " + i public static void main(String args[])
+ " " + j); {
} B subOb = new B(1, 2, 3);
} [Link](); // this calls show() in B
}
}
Contd…
When show( ) is invoked on an object of type B, the version of show( ) defined
within B is used.
That is, the version of show( ) inside B overrides the version
declared in A.
If you wish to access the superclass version of an overridden
method, you can do so by using super.
For example, in this version of B, the superclass version of
show(
class )Bis invoked
extends A { within the subclass’ version.
int k;
B(int a, int b, int c) { If you substitute this version of A
super(a, b); into the previous program, you
k = c; will see the following
}
void show( ) { output:
[Link]( ); // this calls A's i and j: 1 2
show( ) k: 3
[Link]("k: " + k);
}
}
Contd…
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.
// Create a subclass by extending class A.
class B extends A {
// Methods with differing type
int k;
signatures are overloaded – not B(int a, int b, int c)
overridden. {
class A super(a, b);
{ k = c;
int i, j; }
A(int a, int b) // overload show( )
{ void show(String msg) {
[Link](msg + k);
i = a;
}
j = b; }
} class Override {
// display i and j public static void main(String args[ ]) {
void show( ) { B subOb = new B(1, 2, 3);
[Link]("i and j: " + i [Link]("This is k: "); // this calls
+ " " + j); show() in B
} [Link](); // this calls show() in
A
}
}
}
Contd…
The version of show( ) in B takes a string parameter. This makes its type
signature different from the one in A, which takes no parameters.
Therefore, no overriding (or name hiding) takes place. Instead, the version of
show( ) in B simply overloads the version of show( ) in A.
Dynamic Method Dispatch
While the examples in the preceding section demonstrate the mechanics of
method overriding, they do not show its power.
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.
Let’s begin by restating an important principle: a superclass reference variable
can refer to a subclass object.
Java uses this fact to resolve calls to overridden methods at run time. BUT How.
Here is the answer.
When an overridden method is called through a superclass reference, Java
determines which version of that method to execute based upon the type of the
object being referred to at the time the call occurs.
Contd…
Thus, this determination is made at run time. When different types of objects
are referred to, different versions of an overridden method will be called.
In other words, it is the type of the object being referred to (not the type of the
reference variable) that determines which version of an overridden method
will be executed.
Therefore, if a superclass contains a method that is overridden by a subclass,
then when different types of objects are referred to through a superclass
reference variable, different versions of the method are executed.
EXAMPLE PROGRAM
Dynamic Method
Dispatch
This program creates one superclass
called P and two subclasses of it,
called Q and R. Subclasses Q and R
override call( ) declared in P.
Inside the main( ) method, objects
of type P, Q, and R are declared.
Also, a reference of type P, called x,
is declared.
The program then in turn assigns a
reference to each type of object to x
and uses that reference to invoke
call ( ).
As the output shows, the version of
call ( ) executed is determined by
the type of object being referred to at
the time of the call.
29
Why Overridden 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.
Part of the key to successfully applying polymorphism is understanding that the
super classes 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 super class 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
object oriented design brings to bear on code reuse and robustness.
Applying Method Overriding
Let’s look at a more practical example that uses method overriding. The following
program creates a super class called Figure that stores the dimensions of a two-
dimensional object.
It also defines a method called area( ) that computes the area of an object. The
program derives two subclasses from Figure.
The first is Rectangle and the second is Triangle. Each of these subclasses
overrides area( ) so that it returns the area of a rectangle and a triangle,
respectively.
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.
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. So the
subclasses must implement.
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 super class.
You can handle this situation two ways.
One way, as shown in the previous example, is to simply have it report a
warning message.
Contd….
The 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.
The certain methods be overridden by subclasses by specifying the abstract type
modifier.
These methods are sometimes referred to as subclasser responsibility because they
have no implementation specified in the superclass.
Thus, a subclass must override them—it cannot simply use the version defined in
the superclass.
abstract type name(parameter-list);
As you can see, no method body is present.
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.
Contd…
There can be no objects of an abstract class. That is, an abstract class
cannot be directly instantiated with the new operator.
Such objects would be useless, because an abstract class is not fully
defined.
Also, you cannot declare abstract constructors, or abstract static
methods.
Any subclass of an abstract class must either implement all of the
abstract methods in the superclass, or be itself declared abstract.
Although abstract classes cannot be used to instantiate objects, they can
be used to create object references, because Java’s approach to run-time
polymorphism is implemented through the use of super class
references.
Thus, it must be possible to create a reference to an abstract class so
that it can be used to point to a subclass object.
Here is a simple example of a class
with an abstract method, followed
by a class which implements that
method:
Notice that no objects of class A
are declared in the program. As
mentioned, it is not possible to
instantiate an abstract class.
One other point: class A
implements a concrete method
called callmetoo( ). This is
perfectly acceptable.
Contd…
Using an abstract class, you can improve the Figure class shown earlier.
Since there is no meaningful concept of area for an undefined two-
dimensional figure, the following version of the program declares area( ) as
abstract inside Figure.
EXAMPLE
Although it is not possible to create an object of type Figure, you can create
a reference variable of type Figure. The variable figref is declared as a
reference to Figure, which means that it can be used to refer to an object of
any class derived from Figure.
Using final 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. Both are
examined here.
1) Using final to Prevent Overriding
2) Using final to Prevent 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 { class B extends A {
final void meth( ) { void meth( ) { // ERROR! Can't
[Link]("This is a final override.
method."); [Link]("Illegal!");
} }
} }
Contd…
Because meth( ) is declared as final, it cannot be overridden in B. If you attempt
to do so, a compile-time error will result.
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.
As you might expect, it is illegal to declare a class as both abstract and final since
an abstract class is incomplete by itself and relies upon its subclasses to provide
complete implementations.
Here is an example of a final class:
final class A
{ // ...
}
// The following class is illegal.
class B extends A { // ERROR! Can't subclass A
// ...
}
As the comments imply, it is illegal for B to inherit A since A is declared as final.
Local Variable Type Inference and
Inheritance
• A superclass reference can refer to a derived class object in
Java
• 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
– Local variable type inference to the Java language, which is
supported by the context-sensitive keyword var
41
Local Variable Type Inference and Inheritance
43
Object Class
Object’s toString()
• 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
• Doing so allows them to provide a description
specifically for the types of objects that they create
45
46
Object’s equals() and hashCode()
• == is a reference comparison, whether both variables
refer to the same object
• Object’s equals() method does the same thing
• String class override equals() to check contents
• If you want two different objects of a same class to be
equal then you need to override equals() and
hashCode() methods
– hashCode() needs to return same value to work properly as
keys in Hash data structures
47
48
Interfaces
Interfaces
Using the keyword interface, you can fully abstract a class’ interface from its
implementation.
That is, using interface, you can specify what a class must do, but not how it does
it.
Interface are syntactically similar to classes, but they lack instance variables, and
their methods are declared without any body.
This means that you can define interfaces that don’t make assumptions about how
they are implemented. Once it is defined, any number of classes can implement an
interface.
When no access specifier is included, then default access results, and the interface is
only available to other members of the package in which it is declared.
Contd…
When it is declared as public, the interface can be used by any other code.
In this case, the interface must be the only public interface declared in the file,
and the file must have the same name as the interface.
Variables can be declared inside of interface declarations. They are implicitly
final and static, meaning they cannot be changed by the implementing
class. They must also be initialized. All methods and variables are implicitly
public.
Ex: It declares a simple interface that contains one method called call( ) that
takes a single integer
interface Callback
{
void call(int param);
}
Contd…
Contd…
Contd…
Implementing Interfaces
Once an interface has been defined, one or more classes can implement that
interface.
To implement an interface, include the implements clause in a class definition,
and then create the methods defined by the interface.
The general form of a class that includes the implements clause looks like this: