Basic Concept:
Inheritance is considered to be one of the most important concept in Object Oriented Paradigm. In short, it is defined as code reusability. Practically code contains variables, methods and blocks. In Java, inheritance is defined as a concept in which new classes can be produced or derived from existing classes. The newly created class acquires all the features of the existing class. Features includes variables and methods of the existing class. The above concept is used , when a programmer wants some code, which is already written by some other programmer. In Java terminology, a class that is giving its features to other classes is known as super class and the class that does inheriting is known as sub class. In other words, we can define a subclass as a specialized version of super class.
If user wants to inherit the features of a class into his class, then it is possible with the help of extends keyword. In other words, the subclass must contain the extend keyword followed by the name of the class. The syntax of super class and sub class is shown below:
class SuperClassName{
--------------------------------
--------------------------------
--------------------------------
--------------------------------
--------------------------------
--------------------------------
}
class SubClassName extends SuperClassName{
--------------------------------
--------------------------------
--------------------------------
--------------------------------
--------------------------------
--------------------------------
}
In the above program, the sub class will get the variables and methods of the super class also. You can understand this line, after understanding the basic program on inheritance.
Write a program to illustrate the basic concept of Inheritance
class A{
int x=12;
int y=13;
void Add(){
System.out.println("The result of Add method is "+(x+y));
}
}
class B extends A{
int z=16;
void Addition(){
System.out.println("The result of Addition is "+(x+y+z));
}
}
public class Inheritance {
public static void main(String[] args) {
A a1 = new A();
a1.Add();
B b1 = new B();
b1.Addition();
b1.Add();
}
}
Output is: The result of Add method is 25
The result of Addition is 41
The result of Add method 25
Output is: The result of Add method is 25
The result of Addition is 41
The result of Add method 25
In the above program we can notice that, there are three classes. The first is class A, the second in class B and the third is class C. The class A is known as super class, the class B is known as sub class the class C is known as independent class. In class C, we are creating an object of class A as well as we are creating an object of class B. Now let us consider the following statements as shown below:
A a1 = new A();
When the above statement gets executed, a reference variable by name a1 gets created on the stack. When the new operator gets executed, memory gets allocated on the heap. The amount of memory that gets allocated depends on number of instance variables and instance methods that are present with in a class. In class A, we have two instance variables x and y and one instance method known as Add. It must be noted that a1 is a reference variable of super class A.
B b1 = new B();
It must be noted that b1 is a reference variable of class B. When the above statement gets executed, a reference variable by name b1 gets created on the stack. On the execution of the new operator, memory gets allocated on the heap. The amount of memory that gets allocated on the heap depends on the instance variables and instance methods present in class B as well as the instance variables and instance methods present in class A, which is a super class to class B. In class B, we have three instance variables x , y and z and two instance methods Add() and Addition() .In other words , with the help of reference variable b1 we can access the variables x , y and z as well as we can access methods Add() and Addition().
Member Access Rules:
As we know that the sub class object can access the variables and methods of a super class. But the condition is the access specifiers for the variable and methods present in the super class must be either public, protected or default. But, there must be not private. If the variables and methods of a super class are private, then the sub class object cannot access the variables and methods of a super class because the variables and methods that are declared as private, cannot be accessed outside the class in which they are defined. Let us understand the concept of private access specifier with the help of a program as shown below:
class A1{
private int x=10;
void display(){
System.out.println(x);
}
}
public class privateprogram extends A1 {
public static void main(String[] args) {
A1 a2 = new A1();
a2.x=10; //error statement
}
}
Output is:
E:\java>javac privateprogram.java
privateprogram.java:10: x has private access in A1
a2.x=10;
^
1 error
Output is:
E:\java>javac privateprogram.java
privateprogram.java:10: x has private access in A1
a2.x=10;
^
1 error
In the above program there exists two classes, the first is super class A1 and the second is sub class privateprogram. The members that are present in class A1 are instance variable x and instance method display(). The access specifier of the instance variable is private and the access specifier for display() method is default. The sub class is privateprogram that contains a method known as main method. In this method we are creating an object of class A1. The reference variable to class A1 is a2. With the help of a2 , now I want to change the value of x, which is an instance variable in class A1. If we want to change the value, we will get compile time error because we are accessing the variable in another class, which leads to error as we cannot access the variable outside the class A1.
Forms of Inheritance
They are different types of inheritance in Java are:
- Single level Inheritance
- Multi level Inheritance
- Multiple Inheritance
Single level Inheritance
It is defined as an inheritance that consists of only one sub class, in a
hierarchy of classes. We can understand the concept of single level inheritance, with the help of a small example. If some one says that he/ she lives in a single storey building, it means that building consists of ground floor and first floor. Likewise, single level inheritance consists of one super class and one sub class only. To understand the concept of single level inheritance, let us consider the following example as shown below:
class A{
int x=12;
int y=13;
void Add(){
System.out.println("The result of Add method is "+(x+y));
}
}
class B extends A{
int z=16;
void Addition(){
System.out.println("The result of Addition is "+(x+y+z));
}
}
public class Inheritance {
public static void main(String[] args) {
A a1 = new A();
a1.Add();
B b1 = new B();
b1.Addition();
b1.Add();
}
}
Output is:The result of Add method is 25
The result of Addition is 41
The result of Add method is 25
Output is:The result of Add method is 25
The result of Addition is 41
The result of Add method is 25
In the above example, we can notice that there exists only one super class and one sub class.
Multi level Inheritance
It is defined as an inheritance that consists of more than one sub class, in a hierarchy of classes. We can understand the concept of multi level inheritance, with the help of a small example. If some one says that he/ she lives in a multi storey building, it means that building consists of ground floor, first floor, second floor and more than that. Likewise, multi level inheritance consists of more than one super class and more than one sub class . To understand the concept of multi level inheritance, let us consider the following example as shown below:
class A{
int x=12;
int y=13;
void Add(){
System.out.println("The result of Add method is "+(x+y));
}
}
class B extends A{
int z=16;
void Addition(){
System.out.println("The result of Addition is "+(x+y+z));
}
}
class C extends B{
int w=19;
void Addition1(){
System.out.println("The result of Addition1 is"+(x+y+z+w));
}
}
public class Inheritance {
public static void main(String[] args) {
A a1 = new A();
a1.Add();
B b1 = new B();
b1.Addition();
b1.Add();
C c1 = new C();
c1.Addition();
c1.Add();
c1.Addition1();
}
}
Output is: The result of Add method is 25
Output is: The result of Add method is 25
The result of Addition is 41
The result of Add method is 25
The result of Addition is 41
The result of Add method is 25
The result of Addition1 is 60
The result of Add method is 25
The result of Addition is 41
The result of Add method is 25
The result of Addition1 is 60
In the above program, we can notice that there exists more than one sub class and more than one super class, depending on the type of combinations.
Multiple Inheritance
Multiple Inheritance is a type of inheritance that makes a sub class to access variables and methods from more than one super class at a time. Normally, in case of C++, multiple inheritance is possible with the help of extends keyword but where as in Java, multiple inheritance with the help of extends keyword generates a compile time error. That’s why multiple inheritance in Java is possible with the help of implements keyword. Here we have to create more than one interface and then write an implementation class, that can borrow features from more than one interface at a time i.e. after implements keyword we have to write more than two interface names. The following program explains the concept of multiple inheritance in a clear way:
interface A{
void m1();
}
interface B{
void m2();
}
class C implements A,B{
public void m1(){
System.out.println("Method of Interface A");
}
public void m2(){
System.out.println("Method of Interface B");
}
}
public class MutipleInher {
public static void main(String[] args) {
C c1 = new C();
c1.m1();
c1.m2();
}
}
In the above program we can notice that class C is getting features from more than one interface at a time. This concept is known as Multiple Inheritance, which is solved with the help of interfaces concept.
Using final with inheritance
As we know that final is one of the types of access specifier or access modifier that can be used for a variable, method and a class. If we use final for a variable , then the value present in the variable becomes constant i.e. if the value is changed then it leads to compile time error.
One more thing, if variables are declared as final then it is compulsory that variables must be initialized during declaration only. If you initialize the variable in the next line, then it may lead to compile time error.
One more thing, if variables are declared as final then it is compulsory that variables must be initialized during declaration only. If you initialize the variable in the next line, then it may lead to compile time error.
Final with class:
If we use final access specifier for a class, and if the program that contains classes is using inheritance, then we get compile time error. In other words, if we write final access specifier for a super class , and if we try to create sub classes then we get a compile time error saying that classes cannot be inherited. It means that if we declare super class as final, then we cannot create a sub class for that class. if we do so, then a compile time error gets generated.
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!");
}
}
Because meth( ) is declared as final, it cannot be overridden in B. If you attempt to do so, a
compile-time error will result.
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!");
}
}
Because meth( ) is declared as final, it cannot be overridden in B. If you attempt to do so, a
compile-time error will result.
Final with method:
If we use final access specifier for a method, and if the program is following inheritance concept and if user wants to over ride the method then we will get a compile time error. In other words, if we write final access specifier for a method that is present in the super class and if we try to over ride the same method with in a sub class, then we get a compile time error as final methods cannot be overridden i.e. we cannot write methods having same signature as that of a methods in super class with in a sub class.
Method Overriding:
As we have studied that there exists a concept known as Polymorphism in Object Oriented Paradigm. It states that same method can perform different operations at different time. Technically they are two types of polymorphism. The first is known as static polymorphism or Method Overloading and the second is known as dynamic polymorphism or Method Overriding.
Method Overriding will occur only in case of inheritance i.e. if the program is using inheritance, then we can get a problem of method overriding. In case of Method Overloading, the name of the method will be same but the number of parameters with in the method gets changed. But in case of Method Overriding, the name of the method is same as well as the number of parameters are also same. Some times, if program is following a concept known as inheritance, and if the super class as well as sub class contains a method having same name and same number of parameters and same return datatype, then we say that the method present in sub class is over riding the method present in super class. Suppose, we are creating an object of sub class, and if we want to call a method with the help of dot operator, then the method present in sub class only gets executed and the method present in super class never gets executed. It means that during execution of the program, JVM is giving more priority to the method present in sub class than the method present in super class. So, in short we say that the method present in sub class is over riding the method present in super class.
To understand the above concept, lets write program as shown below:
class A{
void display(){
System.out.println("Method display of class A is called");
}
}
class B extends A{
void display(){
System.out.println("Method display of class B is called");
}
}
public class MethodOverriding {
public static void main(String[] args) {
B b1 = new B();
b1.display();
}
}
Output is: Method display of class B is called
Output is: Method display of class B is called
In the above example , we can notice that they are three classes. The first is class A , which is a super class . The second is class B, which is a sub class and the third is MethodOverriding. We can notice that there is a method by name display() that is present in super class as well as sub class has same method name, same return datatype and same number of parameters. But if we create an object of sub class, and if we call display() method, then display() method of class B is called, but if I want to call display() method of class A, which is a super class here, then we cannot call the display() method which is present in super class with the help of object of class B. Here we are getting this problem because the name of the method is same in both super class and sub class. So, if I want to call display() method of super class, then I have to use a keyword known as super which calls a method or variable present in the super class.
Overridden Methods Support
Polymorphism
While the examples in the preceding section demonstrate the mechanics of method overriding, they do not show its power. Indeed, if there were nothing more to method overriding than a name space convention, then it would be, at best, an interesting curiosity but of little real value. However, this is not the case. 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. Here’s how. 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. 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.
Here is an example that illustrates dynamic method dispatch:
// Demonstrate dynamic method dispatch.
class Sup {
void who() {
System.out.println("who() in Sup");
}
}
class Sub1 extends Sup {
void who() {
System.out.println("who() in Sub1");
}
}
class Sub2 extends Sup {
void who() {
System.out.println("who() in Sub2");
}
}
class DynDispDemo {
public static void main(String args[]) {
Sup superOb = new Sup();
Sub1 subOb1 = new Sub1();
Sub2 subOb2 = new Sub2();
Sup supRef;
supRef = superOb;
supRef.who();
supRef = subOb1;
supRef.who();
supRef = subOb2;
supRef.who();
}
}
The output from the program is shown here:
who() in Sup
who() in Sub1
who() in Sub2
This program creates a superclass called Sup and two subclasses of it, called Sub1 and
Sub2. Sup declares a method called who( ), and the subclasses override it. Inside the main( ) method, objects of type Sup, Sub1, and Sub2 are declared. Also, a reference of type Sup, called supRef, is declared. The program then assigns a reference to each type of object to supRef and uses that reference to call who( ). As the output shows, the version of who( ) executed is determined by the type of object being referred to at the time of the call, not by
the class type of supRef.
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 that 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.
Overridden Methods Support
Polymorphism
While the examples in the preceding section demonstrate the mechanics of method overriding, they do not show its power. Indeed, if there were nothing more to method overriding than a name space convention, then it would be, at best, an interesting curiosity but of little real value. However, this is not the case. 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. Here’s how. 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. 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.
Here is an example that illustrates dynamic method dispatch:
// Demonstrate dynamic method dispatch.
class Sup {
void who() {
System.out.println("who() in Sup");
}
}
class Sub1 extends Sup {
void who() {
System.out.println("who() in Sub1");
}
}
class Sub2 extends Sup {
void who() {
System.out.println("who() in Sub2");
}
}
class DynDispDemo {
public static void main(String args[]) {
Sup superOb = new Sup();
Sub1 subOb1 = new Sub1();
Sub2 subOb2 = new Sub2();
Sup supRef;
supRef = superOb;
supRef.who();
supRef = subOb1;
supRef.who();
supRef = subOb2;
supRef.who();
}
}
The output from the program is shown here:
who() in Sup
who() in Sub1
who() in Sub2
This program creates a superclass called Sup and two subclasses of it, called Sub1 and
Sub2. Sup declares a method called who( ), and the subclasses override it. Inside the main( ) method, objects of type Sup, Sub1, and Sub2 are declared. Also, a reference of type Sup, called supRef, is declared. The program then assigns a reference to each type of object to supRef and uses that reference to call who( ). As the output shows, the version of who( ) executed is determined by the type of object being referred to at the time of the call, not by
the class type of supRef.
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 that 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.
Usage of super keyword:
As we have studied that there exists a keyword known as this. It is mainly used in situations when the name of instance variable and the name of a local variable is same that too in the same class. But if the situation is that the program is using a concept known as inheritance and if the name of the method and the name of the variable is same in both sub class and super class, then to access the variables and methods of the super class, we use a keyword known as super keyword. We can use super keyword with a variable and with the method.
super keyword with method:
class A{
void display(){
System.out.println("Method display of class A is called");
}
}
class B extends A{
void display(){
System.out.println("Method display of class B is called");
super.display();
}
}
public class MethodOverriding {
public static void main(String[] args) {
B b1 = new B();
b1.display();
}
}
Output is:Method display of class B is called
Method display of class A is called
Output is:Method display of class B is called
Method display of class A is called
If we want to call the display() method of super class, then it is mandatory that we have to write the statement as super.methodname() in any sub class method, so during execution of the program, JVM will call the method that is present in the immediate super class.
super keyword with variable:
class A{
int x=5;
}
class B extends A{
int x=13;
void dis(){
System.out.println(x);
System.out.println(super.x);
}
}
class C{
public static void main(String args[]){
B b1 = new B();
b1.dis();
}
}
Output is:13
5
Output is:13
5
In the above program, we can notice that the program is using a concept known as inheritance. In this program, there exists three classes. Among three classes, we have one class A, which is known as super class, we have one more class B, which is known as sub class and we have one more class C, which is neither a sub class nor super class, but is said to be an independent class. We can also notice, that there is a variable by name x in super class A and another variable by name x in sub class B. In class C, we created an object of class B , and with the help of this object if we access the variable x, then the x present in class B only gets executed. In other words, with the help of sub class object , we cannot access the variable x that is present in super class. In order to access the variable x present in super class, we have to use a keyword super with the name of the variable. Here we write
System.out.println(super.x); in a method of a sub class.
When the above statement gets executed, the variable present in super class is called.
Using super to Access Superclass Members
There is a second form of super that 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:
super.member
Here, member can be either a method or an instance variable. This form of super is most applicable to situations in which member names of a subclass
hide members by the same name in the superclass. Consider this simple class hierarchy:
// Using super to overcome name hiding.
class A {
int i;
}
// Create a subclass by extending class A.
class B extends A {
int i; // this i hides the i in A
B(int a, int b) {
super.i = a; // i in A
i = b; // i in B
}
void show() {
System.out.println("i in superclass: " + super.i);
System.out.println("i in subclass: " + i);
}
}
class UseSuper {
public static void main(String args[]) {
B subOb = new B(1, 2);
subOb.show();
}
}
This program displays the following:
i in superclass: 1
i in subclass: 2
Although the instance variable i in B hides the i in A, super allows access to the i defined in the superclass. super can also be used to call methods that are hidden by a subclass.
Extending the Vehicle Class
To illustrate the power of inheritance, we will extend the Vehicle class
first developed in Module 4. As you should recall, Vehicle encapsulates
information about vehicles, including the number of passengers they can carry, their fuel capacity, and fuel consumption rate. We can use the Vehicle class as a starting point from which more specialized classes are developed. For example, one type of vehicle is a truck. An important attribute of a truck is its cargo capacity. Thus, to create a Truck class, you can extend Vehicle, adding an instance variable that stores the carrying capacity. Here is a version
of Vehicle that does this. In the process, the instance variables in Vehicle will be made private, and accessor methods are provided to get and set their values.
Step by Step
1. Create a file called TruckDemo.java and copy the last implementation of Vehicle from
Module 4 into the file.
2. Create the Truck class as shown here.
// Extend Vehicle to create a Truck specialization.
class Truck extends Vehicle {
private int cargocap; // cargo capacity in pounds
// This is a constructor for Truck.
Truck(int p, int f, int m, int c) {
/* Initialize Vehicle members using
Vehicle's constructor. */
super(p, f, m);
cargocap = c;
}
// Accessor methods for cargocap.
int getCargo() { return cargocap; }
void putCargo(int c) { cargocap = c; }
}
Here, Truck inherits Vehicle, adding cargocap, getCargo( ), and putCargo( ). Thus, Truck includes all of the general vehicle attributes defined by Vehicle. It need add only those items that are unique to its own class.
3. Next, make the instance variables of Vehicle private, as shown here.
private int passengers; // number of passengers
private int fuelcap; // fuel capacity in gallons
private int mpg; // fuel consumption in miles per gallon
4. Here is an entire program that demonstrates the Truck class.
// Build a subclass of Vehicle for trucks.
class Vehicle {
private int passengers; // number of passengers
private int fuelcap; // fuel capacity in gallons
private int mpg; // fuel consumption in miles per gallon
// This is a constructor for Vehicle.
Vehicle(int p, int f, int m) {
passengers = p;
fuelcap = f;
mpg = m;
}
// Return the range.
int range() {
return mpg * fuelcap;
}
// Compute fuel needed for a given distance.
double fuelneeded(int miles) {
return (double) miles / mpg;
}
// Access methods for instance variables.
int getPassengers() { return passengers; }
void setPassengers(int p) { passengers = p; }
int getFuelcap() { return fuelcap; }
void setFuelcap(int f) { fuelcap = f; }
int getMpg() { return mpg; }
void setMpg(int m) { mpg = m; }
}
// Extend Vehicle to create a Truck specialization.
class Truck extends Vehicle {
private int cargocap; // cargo capacity in pounds
// This is a constructor for Truck.
Truck(int p, int f, int m, int c) {
/* Initialize Vehicle members using
Vehicle's constructor. */
super(p, f, m);
cargocap = c;
}
// Accessor methods for cargocap.
int getCargo() { return cargocap; }
void putCargo(int c) { cargocap = c; }
}
class TruckDemo {
public static void main(String args[]) {
// construct some trucks
Truck semi = new Truck(2, 200, 7, 44000);
Truck pickup = new Truck(3, 28, 15, 2000);
double gallons;
int dist = 252;
gallons = semi.fuelneeded(dist);
System.out.println("Semi can carry " + semi.getCargo() +
" pounds.");
System.out.println("To go " + dist + " miles semi needs " +
gallons + " gallons of fuel.\n");
gallons = pickup.fuelneeded(dist);
System.out.println("Pickup can carry " + pickup.getCargo() +
" pounds.");
System.out.println("To go " + dist + " miles pickup needs " +
gallons + " gallons of fuel.");
}
}
5. The output from this program is shown here:
Semi can carry 44000 pounds.
To go 252 miles semi needs 36.0 gallons of fuel.
Pickup can carry 2000 pounds.
To go 252 miles pickup needs 16.8 gallons of fuel.
Using super to Access Superclass Members
There is a second form of super that 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:
super.member
Here, member can be either a method or an instance variable. This form of super is most applicable to situations in which member names of a subclass
hide members by the same name in the superclass. Consider this simple class hierarchy:
// Using super to overcome name hiding.
class A {
int i;
}
// Create a subclass by extending class A.
class B extends A {
int i; // this i hides the i in A
B(int a, int b) {
super.i = a; // i in A
i = b; // i in B
}
void show() {
System.out.println("i in superclass: " + super.i);
System.out.println("i in subclass: " + i);
}
}
class UseSuper {
public static void main(String args[]) {
B subOb = new B(1, 2);
subOb.show();
}
}
This program displays the following:
i in superclass: 1
i in subclass: 2
Although the instance variable i in B hides the i in A, super allows access to the i defined in the superclass. super can also be used to call methods that are hidden by a subclass.
Extending the Vehicle Class
To illustrate the power of inheritance, we will extend the Vehicle class
first developed in Module 4. As you should recall, Vehicle encapsulates
information about vehicles, including the number of passengers they can carry, their fuel capacity, and fuel consumption rate. We can use the Vehicle class as a starting point from which more specialized classes are developed. For example, one type of vehicle is a truck. An important attribute of a truck is its cargo capacity. Thus, to create a Truck class, you can extend Vehicle, adding an instance variable that stores the carrying capacity. Here is a version
of Vehicle that does this. In the process, the instance variables in Vehicle will be made private, and accessor methods are provided to get and set their values.
Step by Step
1. Create a file called TruckDemo.java and copy the last implementation of Vehicle from
Module 4 into the file.
2. Create the Truck class as shown here.
// Extend Vehicle to create a Truck specialization.
class Truck extends Vehicle {
private int cargocap; // cargo capacity in pounds
// This is a constructor for Truck.
Truck(int p, int f, int m, int c) {
/* Initialize Vehicle members using
Vehicle's constructor. */
super(p, f, m);
cargocap = c;
}
// Accessor methods for cargocap.
int getCargo() { return cargocap; }
void putCargo(int c) { cargocap = c; }
}
Here, Truck inherits Vehicle, adding cargocap, getCargo( ), and putCargo( ). Thus, Truck includes all of the general vehicle attributes defined by Vehicle. It need add only those items that are unique to its own class.
3. Next, make the instance variables of Vehicle private, as shown here.
private int passengers; // number of passengers
private int fuelcap; // fuel capacity in gallons
private int mpg; // fuel consumption in miles per gallon
4. Here is an entire program that demonstrates the Truck class.
// Build a subclass of Vehicle for trucks.
class Vehicle {
private int passengers; // number of passengers
private int fuelcap; // fuel capacity in gallons
private int mpg; // fuel consumption in miles per gallon
// This is a constructor for Vehicle.
Vehicle(int p, int f, int m) {
passengers = p;
fuelcap = f;
mpg = m;
}
// Return the range.
int range() {
return mpg * fuelcap;
}
// Compute fuel needed for a given distance.
double fuelneeded(int miles) {
return (double) miles / mpg;
}
// Access methods for instance variables.
int getPassengers() { return passengers; }
void setPassengers(int p) { passengers = p; }
int getFuelcap() { return fuelcap; }
void setFuelcap(int f) { fuelcap = f; }
int getMpg() { return mpg; }
void setMpg(int m) { mpg = m; }
}
// Extend Vehicle to create a Truck specialization.
class Truck extends Vehicle {
private int cargocap; // cargo capacity in pounds
// This is a constructor for Truck.
Truck(int p, int f, int m, int c) {
/* Initialize Vehicle members using
Vehicle's constructor. */
super(p, f, m);
cargocap = c;
}
// Accessor methods for cargocap.
int getCargo() { return cargocap; }
void putCargo(int c) { cargocap = c; }
}
class TruckDemo {
public static void main(String args[]) {
// construct some trucks
Truck semi = new Truck(2, 200, 7, 44000);
Truck pickup = new Truck(3, 28, 15, 2000);
double gallons;
int dist = 252;
gallons = semi.fuelneeded(dist);
System.out.println("Semi can carry " + semi.getCargo() +
" pounds.");
System.out.println("To go " + dist + " miles semi needs " +
gallons + " gallons of fuel.\n");
gallons = pickup.fuelneeded(dist);
System.out.println("Pickup can carry " + pickup.getCargo() +
" pounds.");
System.out.println("To go " + dist + " miles pickup needs " +
gallons + " gallons of fuel.");
}
}
5. The output from this program is shown here:
Semi can carry 44000 pounds.
To go 252 miles semi needs 36.0 gallons of fuel.
Pickup can carry 2000 pounds.
To go 252 miles pickup needs 16.8 gallons of fuel.