0% found this document useful (0 votes)
22 views53 pages

Chapter 3 Inheritance and Polymorphism

The document discusses inheritance and polymorphism in object-oriented programming. It explains that inheritance allows the creation of class hierarchies where subclasses inherit attributes and behaviors from superclasses. Subclasses can override or extend the methods of their superclasses, enabling polymorphism and different objects to respond to the same method in different ways.

Uploaded by

abrhamashenafi3
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF, TXT or read online on Scribd
0% found this document useful (0 votes)
22 views53 pages

Chapter 3 Inheritance and Polymorphism

The document discusses inheritance and polymorphism in object-oriented programming. It explains that inheritance allows the creation of class hierarchies where subclasses inherit attributes and behaviors from superclasses. Subclasses can override or extend the methods of their superclasses, enabling polymorphism and different objects to respond to the same method in different ways.

Uploaded by

abrhamashenafi3
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF, TXT or read online on Scribd
You are on page 1/ 53

Inheritance and Polymorphism

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, unique elements
Inheritance examples
Superclass: Subclasses
Student: GraduateStudent, UndergraduateStudent
Shape: Circle, Triangle, Rectangle
Loan: CarLoan, HomeImprovementLoan, MortgageLoan
Employee: Faculty, Staff
BankAccount: CheckingAccount, SavingsAccount
Inheritance Basics
To inherit a class, you simply incorporate the definition of one
class into another by using the
extends keyword. To see how, let’s begin with a short example.
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.
// A simple example of inheritance.
// Create a superclass.
class A {
int i, j;
void showij() {
System.out.println("i and j: " + i + " " + j);
}
}
...cont.

// Create a subclass by extending class A.


class B extends A {
int k;
void showk() {
System.out.println("k: " + k);
}
void sum() {
System.out.println("i+j+k: " + (i+j+k));
}
}
...cont.
class SimpleInheritance {
public static void main(String args[]) {
A superOb = new A();AVA LANGUAGE
B subOb = new B();
// The superclass may be used by itself.
superOb.i = 10;
superOb.j = 20;
System.out.println("Contents of superOb: ");
superOb.showij();
System.out.println();
// The subclass has access to all public members of its superclass.
subOb.i = 7;
subOb.j = 8;
subOb.k = 9;
...cont.
System.out.println("Contents of subOb: ");
subOb.showij();
subOb.showk();
System.out.println();
System.out.println("Sum of i, j and k in subOb:");
subOb.sum();
}
}
The output from this program is shown here:
Contents of superOb:
i and j: 10 20
...cont.
Contents of subOb:
i and j: 7 8
k: 9
Sum of i, j and k in subOb:
i+j+k: 24
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.
The general form of a class declaration that inherits a superclass is shown
here:
class subclass-name extends superclass-name {
// body of class
}
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.
For example, consider the following simple class hierarchy:
/* In a class hierarchy, private members remain private to their class. This program
contains an error and will not compile.*/
// 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);
subOb.sum();
System.out.println("Total is " + subOb.total);
}
}
This program will not compile because the reference to 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.
...cont.
A class member that has been declared as private will remain
private to its class. It is not
accessible by any code outside its class, including subclasses.
A subclass can change the state of private superclass instance
variables only through non_x0002_private methods provided in
the superclass and inherited by the subclas.

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. You
will find this aspect of inheritance quite useful in a variety
of situations.
For example, consider the following:
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();
...cont.
// 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);
}
}
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.
protected Members
A class’s public members are accessible wherever the program
has a reference to an object of that class or one of its
subclasses. A class’s private members are accessible only
from within the class itself. A superclass’s private members
are not inherited by its subclasses. Using protected access
offers an intermediate level of access between public and
private.
A superclass’s protected members can be accessed by members
of that superclass, by members of its
subclasses and by members of other classes in the same
package (i.e., protected members also have
package access).
Casting
In Java, type casting is a method or process that converts a one
data type into another data type in both ways manually and
automatically. The automatic conversion is done by the compiler
and manual conversion performed by the programmer.
Type casting and its types in the diagram.

figure 1: type cating in java


Narrowing Type Casting:
converting a higher data type into a lower one. It is also
known as explicit conversion or casting up. It is done
manually by the programmer. If we do not perform casting
then the compiler reports a compile-time error.
public class NarrowingTypeCasting {
public static void main(String args[]) {
double d = 12.76;
//converting double data type into long data type
long l = (long)d;
//converting long data type into int data type
int i = (int)l;
System.out.println("Before conversion: "+d);
//fractional part lost
System.out.println("After conversion into long type: "+l);
//fractional part lost
System.out.println("After conversion into int type: "+i);
}
}
Output
Before conversion: 12.76
After conversion into long type: 12
After conversion into int type: 12
converting a lower data type into a higher one. It is also
known as implicit conversion or casting down. It is done
automatically. It is safe because there is no chance to lose
data. It takes place when:Both data types must be compatible
with each other.The target type must be larger than the
source type.
For example, the conversion between numeric data type to
char or Boolean is not done
automatically.
Also, the char and Boolean data types are not compatible
with each other
...cont
public class WideningTypeCasting {
public static void main(String[] args) {
int x =21;
//automatically converts the integer type into long type
long y = x;
//automatically converts the long type into float type
float z = y;
System.out.println("Before conversion, int value "+x);
System.out.println("After conversion, long value "+y);
System.out.println("After conversion, float value "+z);
}
}
output?
Polymorphism
Polymorphism in Java is a concept by which we can perform a
single action in different ways. Polymorphism is derived from
2 Greek words: poly and morphs. The word "poly" means many and
"morphs" means forms. So polymorphism means many forms.
There are two types of polymorphism in Java: compile-time
polymorphism and runtime
polymorphism. We can perform polymorphism in java by method
overloading and method overriding.
If you overload a static method in Java, it is the example of
compile time polymorphism.
Here, we will focus on runtime polymorphism in java.
Java Polymorphism
Polymorphism means "many forms", and it occurs when we have
many classes that are related to each other by inheritance.
Inheritance lets us inherit attributes and methods from
another class. Polymorphism uses those methods to perform
different tasks. This allows us to perform a single action in
different ways.
For example, think of a superclass called Animal that has a
method called animalSound(). Subclasses of Animals could be
Pigs, Cats, Dogs, Birds - And they also have their own
implementation of an animal sound (the pig oinks, and the cat
meows, etc.):
Class Animal {
public void animalSound() {
System.out.println("The animal makes a sound");
}
}
class Pig extends Animal {
public void animalSound() {
System.out.println("The pig says: wee wee");
}
}
class Dog extends Animal {
public void animalSound() {
System.out.println("The dog says: bow wow");
}
}
Method Overriding and Overloading
Method Overriding:If subclass (child class) has the same method
as declared in the parent class, it is known as method
overriding in Java.In other words, if a subclass provides the
specific implementation of the method that has been declared by
one of its parent class,it is known as method overriding.
If subclass (child class) has the same method as declared in
the parent class, it is known as method overriding in Java.
In other words, if a subclass provides the specific
implementation of the method that has been declared by one of
its parent class, it is known as method overriding
Usage of Java Method Overriding
Method overriding is used for runtime polymorphism

üThe method must have the same name as in the parent class
üThe method must have the same parameter as in the parent class.
üThere must be an IS-A relationship (inheritance).
When an overridden method is called from within a subclass, it will
always refer to the version of that method defined by the subclass.
The version of the method defined by the superclass will be hidden.
Consider the following:
// 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 {
THE JAVA LANGUAGE
int k;
B(int a, int b, int c) {
super(a, b);
k = c;
}
// display k – this overrides show() in A
void show() {
System.out.println("k: " + k);
}
}
class Override {
public static void main(String args[]) {
B subOb = new B(1, 2, 3);
subOb.show(); // this calls show() in B
}
}
The output produced by this program is shown here:
k: 3
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 function, you can do so by using
super. For example, in this version of B, the superclass
version of show( ) is invoked within the
subclass’version.This allows all instance variables to be
displayed.
...cont
class B extends A {
int k;
B(int a, int b, int c) {
super(a, b);
k = c;
}
void show() {
super.show(); // this calls A's show()
System.out.println("k: " + k);
}
}
OutPut: i and j: 1 2
k: 3
...cont.
// Methods with differing type signatures are overloaded – not
overridden.
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);
}
}
// Create a subclass by extending class A.
...cont.
class B extends A {
int k;
B(int a, int b, int c) {
super(a, b);
k = c;THE JAVA LANGUAGE
}
// overload show()
void show(String msg) {
System.out.println(msg + k);
}
}
...cont.
class Override {
public static void main(String args[]) {
B subOb = new B(1, 2, 3);
subOb.show("This is k: "); // this calls show() in B
subOb.show(); // this calls show() in A
}
}
The output produced by this program is shown here:
This is k: 3
i and j: 1 2
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.
Why Overridden Methods?
It allow Java to support run-time polymorphism.
it’s another way that Java implements the “one interface,
multiple methods” aspect of polymorphism.
Used correctly, the superclass provides all elements that a
subclass can use directly.
This allows the subclass the flexibility to define its own
methods, yet still enforces a consistent interface.
Dynamic, run-time polymorphism is one of the most powerful
mechanisms that object_x0002_oriented 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.
Overloading Methods
In Java it is possible to define two or more methods within
the same class that share the same name, as long as their
parameter declarations are different. When this is the case,
the methods are said to be overloaded, and the process is
referred to as method overloading.
Java uses the type and/or number of arguments as its guide
to determine which version of the overloaded method to
actually call.Thus, overloaded methods must differ in the
type and/or number of their parameters.
Here is a simple example that illustrates method overloading:
// Demonstrate method overloading.
class OverloadDemo {
void test() {
System.out.println("No parameters");
}
// Overload test for one integer parameter.
void test(int a) {
System.out.println("a: " + a);
}
// Overload test for two integer parameters.
void test(int a, int b) {
System.out.println("a and b: " + a + " " + b);
}
...cont
// overload test for a double parameter
double test(double a) {
System.out.println("double a: " + a);
return a*a;
}
}
class Overload {
public static void main(String args[]) {
OverloadDemo ob = new OverloadDemo();
double result;
// call all versions of test()
}
...cont.
ob.test();
ob.test(10);
ob.test(10, 20);
result = ob.test(123.25);
System.out.println("Result of ob.test(123.25): " + result);
}
}
This program generates the following output:
No parameters
a: 10
a and b: 10 20
double a: 123.25
Result of ob.test(123.25): 15190.5625
As you can see, test( ) is overloaded four times. The
first version takes no parameters, the second
takes one integer parameter, the third takes two integer
parameters, and the fourth takes one double parameter.

The fact that the fourth version of test( ) also returns a


value is of no consequence relative to overloading, since
return types do not play a role in overload resolution.
When an overloaded method is called, Java looks for a
match between the arguments used to call the method and
the method’s parameters.
However, this match need not always be exact. In some
cases Java’s automatic type conversions can play a role
in overload resolution.
For example, consider the following program:
// Automatic type conversions apply to overloading.
class OverloadDemo {
void test() {
System.out.println("No parameters");
}
// Overload test for two integer parameters.
void test(int a, int b) {
System.out.println("a and b: " + a + " " + b);
}
// overload test for a double parameter
void test(double a) {
System.out.println("Inside test(double) a: " + a);
}
}
...cont.
class Overload {
public static void main(String args[]) {
OverloadDemo ob = new OverloadDemo();
int i = 88;
ob.test();
ob.test(10, 20);
ob.test(i); // this will invoke test(double)
ob.test(123.2); // this will invoke test(double)
}
}
This program generates the following output:
No parameters
a and b: 10 20
Inside test(double) a: 88
Inside test(double) a: 123.2
...cont.
As you can see, this version of OverloadDemo does not define
test(int). Therefore, when test( )
is called with an integer argument inside Overload, no
matching method is found. However, Java
can automatically convert an integer into a double, and this
conversion can be used to resolve the
call. Therefore, after test(int) is not found, Java elevates
i to double and then calls test(double).
Of course, if test(int) had been defined, it would have been
called instead. Java will employ its
automatic type conversions only if no exact match is found
Overloading Constructors
In addition to overloading normal methods, you can also
overload constructor methods. In fact,
for most real-world classes that you create, overloaded
constructors will be the norm, not the
exception. To understand why, let’s consider the Box class.
Following is the latest version of Box:
class Box {
double width;
double height;
double depth;
// This is the constructor for Box.
Box(double w, double h, double d) {
...cont.
width = w;
height = h;
depth = d;
}
// compute and return volume
double volume() {
return width * height * depth;
}
}
As you can see, the Box( ) constructor requires three parameters.
This means that all declarations of Box objects must pass three
arguments to the Box( ) constructor. For example, the following
statement is currently invalid:
Box ob = new Box();
A Closer Look at Argument Passing
In general, there are two ways that a computer language can
pass an argument to a subroutine.
1. Call-by-value:- This method copies the value of an argument
into the formal parameter
of the subroutine. Therefore, changes made to the parameter of
the subroutine have no effect on the argument.
2. Call-by-reference: - In this method, a reference to an
argument (not the value of the argument) is passed to the
parameter. Inside the subroutine, this reference is used to
access the actual argument specified in the call. This means
that changes made to the parameter will affect the argument
used to call the subroutine.
For example, consider the following program:
// Simple types are passed by value.
class Test {
void meth(int i, int j) {
i *= 2;
j /= 2;
}
}
class CallByValue {
public static void main(String args[]) {
Test ob = new Test();
int a = 15, b = 20;
System.out.println("a and b before call: " + a + " " + b);
ob.meth(a, b);
...cont.
System.out.println("a and b after call: " + a + " " + b);
}
}
The output from this program is shown here:
a and b before call: 15 20
a and b after call: 15 20
As you can see, the operations that occur inside meth( )
have no effect on the values of a and b used in the call;
their values here did not change to 30 and 10
call-by-reference
// Objects are passed by reference.
class Test {
int a, b;
Test(int i, int j) {
a = i;
b = j;
}
// pass an object
void meth(Test o) {
o.a *= 2;
o.b /= 2;
}
}
...cont.
public static void main(String args[]) {
Test ob = new Test(15, 20);
System.out.println("ob.a and ob.b before call: " + ob.a + " " + ob.b);
ob.meth(ob);
System.out.println("ob.a and ob.b after call: " + ob.a + " " + ob.b);
}
}
This program generates the following output:
ob.a and ob.b before call: 15 20
ob.a and ob.b after call: 30 10
As you can see, in this case, the actions inside meth( ) have affected the object
used as an argument.
As a point of interest, when an object reference is passed to a method, the
reference itself is passed
by use of call-by-value.
Returning Objects
A method can return any type of data, including
class types that you create. For example, in the
following program, the incrByTen( ) method returns
an object in which the value of a is ten greater
than it is in the invoking object.
// Returning an object.
class Test {
int a;
Test(int i) {
a = i;
}
Test incrByTen() {
Test temp = new Test(a+10);
return temp;
}
}
class RetOb {
public static void main(String args[]) {
Test ob1 = new Test(2);
Test ob2;
ob2 = ob1.incrByTen();
System.out.println("ob1.a: " + ob1.a);
System.out.println("ob2.a: " + ob2.a);
ob2 = ob2.incrByTen();
System.out.println("ob2.a after second increase: "+ ob2.a);
}
}
The output generated by this program is shown here:
ob1.a: 2
ob2.a: 12
ob2.a after second increase: 22
As you can see, each time incrByTen( ) is invoked, a new object is
created, and a reference to it
is returned to the calling routine.
The preceding program makes another important point: Since all
objects are dynamically
allocated using new, you don’t need to worry about an object going
out-of-scope because the
method in which it was created terminates. The object will continue
to exist as long as there is a
reference to it somewhere in your program. When there are no
references to it, the object will be
reclaimed the next time garbage collection takes place.

You might also like