CHAPTER 16

INHERITANCE REVISITED

We have studied the basic concepts in inheritance in Chapter 12. Now, let us study a few advanced concepts.

Java is a highly object-oriented language. It is most suitable for studying object orientation. Java has class Object as the base (super) class for every other class. Hence, it is a good idea to study this class before we study advanced concepts in inheritance.

16.1 Class Object

In Java, a public class Object is at the root of the class hierarchy. Every class in Java whether defined by us or supplied by various packages has this class as a superclass. The methods of this class are implemented by all classes including arrays.

Table 16.1 illustrates various methods from this class.

 

Table 16.1 Method Summary: Class Object

protected Object clone() Creates and returns a copy of this object.
boolean equals(Object obj) Indicates whether some other object is “equal to” this one.
protected void finalize() Called by the garbage collector on an object when garbage collection determines that there are no more references to the object.
Class<? extends Object> getClass() Returns the runtime class of an object.
int hashCode() Returns a hash code value for the object.
void notify() Wakes up a single thread that is waiting on this object’s monitor.
void notifyAll() Wakes up all threads that are waiting on this object’s monitor.
String toString() Returns a string representation of the object.
void wait() Causes current thread to wait until another thread invokes the notify() method or the notifyAll() method for this object.
void wait(long timeout Causes current thread to wait until either another thread invokes the notify() method or the notifyAll() method for this object, or a specified amount of time has elapsed.
void wait(long timeout, int nanos Causes current thread to wait until another timeout, int thread invokes the notify() method or nanos) the notifyAll() method for this object, or some other thread interrupts the current thread, or a certain amount of real time has elapsed.

When we started studying Java, we did not start with this class. Hence, while studying, we had many unanswered questions. We wondered why Java behaves like this. Many of these questions will now be answered.

Let us first study the method toString(). This method returns a string representation of the object. When we define a class say Point, we may not redefine this method. But when we write a code like

System.out.println( p1)

something is printed on the screen. This is because when method println() prints an object, it actually prints the string returned by the toString() of the point. As point is a sub-class of class Object, println invokes this method.

Similarly, when we say s1.equals(s2), it returns correct Boolean result when s1 and s2 are Strings. This is because class String has overridden equals() method. If we try

p1.equals(p2)

the code will compile and may give wrong result. If we want accurate result, our class point must override the equals() method.

Let us study this with simple programs.

Problem: Write a program to demonstrate that when not defined, the class uses its super-class objects methods equals() and toString().

Solution: See Program 16.1.

 

PROGRAM 16.1 Methods from Class Object

 //     point1.java

 class point1

 {

   public static void main(String args[])

     {   point p1,p2 ;

         System.out.println("<---point1.java--->");

         p1 = new point(3.0,2.0);

         p2 = new point(3.0,2.0);

         System.out.println(p1);

         System.out.println( p1.equals(p2) );

     }

 }

 class point

    {  private double x,y ;

       point(double a, double b)

            {   x=a;y=b;}

     };

 

If you run this program, you will get the following output.

 

Output

 <---point1.java--->

 point@82ba41

 false

Problem: Rewrite Program 16.1 with class point defining overriding methods toString() and equals().

Solution: See Program 16.2.

 

PROGRAM 16.2 Overriding Methods of Class Object

 //     pointpoint2.java

 class point2

 {

   public static void main(String args[])

     { point p1,p2 ;

       System.out.println("<---point2.java--->");

       p1 = new point(3.0,2.0);

       p2 = new point(3.0,2.0);

       System.out.println("p1 = " + p1);

       System.out.println("p2 = " + p2);

       System.out.println( "They are equal : "

                              + p1.equals(p2) );

    }

 }

 class point

    {  private double x,y ;

       point(double a, double b)

            { x=a;y=b;}

       public String toString()

        { return ( "point["+ String.valueOf(x) + " , " +

                   String.valueOf(y)+" ]" ) ;

        }

       public boolean equals( point p2)

        { if( ( this.x == p2.x) && (this.y ==p2.y) )

                           return true;

               else        return false ;

        }

    };

 

If you run this program, you will get the following output.

 

Output

 <---point2.java--->

 p1 = point[3.0 , 2.0 ]

 p2 = point[3.0 , 2.0 ]

 They are equal : true

 

In the above program, we have overridden the method equals(). Remove this method from the above program and compile and run the program. You will get results that points are not equal. The points p1 and p2 are different objects. The method of class Object does not check for equality of the contents. This task is left to us the programmers!

The methods wait(), notify(), and notifyAll() work with threads. We will be discussing threads in Chapter 18. The method hashCode() returns a hashcode which is an integer. This code is very useful in searching and sorting. You will see classes like HashTable and HashSet that are discussed in Chapter 23.

16.2 Multiple Inheritance

In C++ a class can have two different classes as its super-classes. Such a scheme of inheritance is termed as multiple inheritance. Java does not allow this type of inheritance.

This has been shown symbolically in Figure 16.1.

Figure 16.1 Multiple Inheritance not Allowed

Figure 16.1 Multiple Inheritance not Allowed

However, Java allows a class to extend one class and implement one (or more) interface. This is shown in Figure 16.2.

In the opinion of many experts, this qualifies for calling multiple inheritance. We feel that this should not be called as multiple inheritance. When a class extends another class, it has access to all the fields and has a free use of the already developed methods. When you implement an interface, you only get what is to be developed. No cooked food!

Figure 16.2 Multiple Inheritance in Terms of Inheritance

Figure 16.2 Multiple Inheritance in Terms of Inheritance

In our opinion, we hardly need the facility of multiple inheritance. Its absence cannot be considered as a disadvantage.

16.3 Polymorphism

One of the useful features of object-oriented programming is polymorphism. It is the ability of the objects to take different forms. The ability to display variable behaviour depending on the situation is a great facility in any programming language.

All early texts on “Object Technology” discussed the following situation. Consider a case when we define shape as a super-class and triangle, rectangle, and hexagon as its sub-classes. If we have a method draw() in super-class shape, it will be redefined in all the sub-classes. Now when we invoke draw on subclass, the triangle program will draw a triangle and class rectangle will draw rectangle, that is different action for the same method. We know that message passing is by means of method call in Java. So we see that every sub-class is responding appropriately to the same message draw(). This is precisely the meaning of polymorphism with reference to a programming language.

At first sight, it appears to be absolutely normal. A method invoked for a particular class has to execute its own class method. For the first time a strictly typed language like Java is allowing a little freedom to its users. It will allow us to assign a sub-class reference value to a variable (reference) of super-class. As in the above example, a variable reference of type shape can refer to variable of type triangle. And when you invoke draw, it does not draw a shape but a triangle.

Before continuing with polymorphism, let us study the intricacies of assigning subclass reference to super-class variable.

We have said that Java allows super-class reference to point to sub-class object. However, its converse is not true. Java does not allow sub-class reference to point to super-class object. These rules are shown pictorially in Figure 16.3.

Figure 16.3 Super- and Sub-class References

Figure 16.3 Super- and Sub-class References

16.3.1 A super-class variable can reference a sub-class object

We are familiar with both super-class and sub-class. We can define reference variables for both the classes. We have seen assignment operation working with reference variables as well. So far, we have assigned one reference variable to another reference variable of the same class.

Now consider the following declarations:

Top king1, king2;

Bottom joker1, joker2;

where class Bottom extends class Top

Here assignment

king1 = king2 ;

is fine; What about the following assignment?

king1 = joker1 ;

Java is a strict language. Will it allow a variable of super-class to be assigned a value of sub-class reference? Surprisingly, the answer is yes. If this happens, such variables points to sub-class object. However, there are certain restrictions. Such variable can reference only that data and code which relate to super-class only.

Problem: Write a program to demonstrate that super-class reference can be assigned the value of sub-class reference.

Solution: See Program 16.3.

 

PROGRAM 16.3 Super-class Reference Points to Sub-class Object I

 //     inher5.java

 class Top

       {   String st1 ;

           Top(String sx)

            { st1 = sx ; }

       } ;

 class Bottom extends Top

       {   String st2;

           Bottom(String sx , String sy)

            { super(sx);

              st2 = sy ;

            }

       }

 class inher5

 {   public static void main (String argv[])

     { Top t1 ;

       System.out.println("<---inher5.java--->");

       Bottom b1 = new Bottom("Hero", "Joker") ;

       System.out.println("-st1 is -->"+ b1.st1) ;

       System.out.println("-st2 is -->"+ b1.st2) ;

       t1 = b1 ;

       System.out.println("-st1 is -->"+ t1.st1) ;

       System.out.println("-st2 is -->"+ t1.st2) ;// Note 1

     };

 }

 

If you compile the program, you get an error.

 

Compilation Error

 inher5.java:26: cannot find symbol

 symbol: variable st2

 location: class Top

 System.out.println("-st2 is -->"+ t1.st2) ;// Note 1

                                     ^

 1 error

 press any key to continue

 

If you remove the line marked note1 (and rename the program as inher5A), you will get the following output.

 

Output

 <---inher5a.java--->

 -st1 is -->Hero

 -st2 is -->Joker

 -st1 is -->Hero

It may be noted that reference to class TOP t1 is assigned a value b1, which is a reference to class BOTTOM. After this, we are allowed to refer to those variables which are defined in super-class. We are not allowed to refer to those variables which are defined in sub-class.

Figure 16.4 clearly illustrates this principle.

Figure 16.4 Super-class Reference Points to Sub-class

Figure 16.4 Super-class Reference Points to Sub-class

This is fine for field variables, but what about methods? Fundamentally, only those methods which are defined in super-class are usable. This is fine, but many times subclasses have overridden methods from super-class. This means that with a given method name, there are two codes: one written in super-class definition and another in sub-class definition. Which code will be executed? The answer is the code from sub-class is executed.

Let us verify this fact with the following program.

Problem: Write a program to demonstrate that when a super-class reference is assigned a value of sub-class variable, it refers to methods of sub-class.

Solution: See Program 16.4.

 

PROGRAM 16.4 Super-class Reference Points to Sub-class Object II

 //     inher15.java

 class TOP

   {  String st1 ;

      void init1(String sx )

      { st1 = sx ; } ;

        public void show()

         { System.out.println("method show(): from TOP "

                 + st1) ;

         };

      } ;

 class BOTTOM extends TOP

   { String st2;

     void init2 (String sy)

       {st2 = sy ; }

       public void show()

        {System.out.println("method show(): from BOTTOM "

                          + st2) ;

        }

   }

 class inher15

  { public static void main (String argv[])

     { TOP t1 ;

       System.out.println("<---inher15.java-->");

       BOTTOM b1 = new BOTTOM() ;

       b1.init2("Joker");

       b1.init1("Hero");

       b1.show();

       t1 = b1 ;

       t1.show();

     };

 }

 

If we run this program, we will get the following output.

 

Output

 <---inher15.java-->

 method show(): from BOTTOM Joker

 method show(): from BOTTOM Joker

 

Note the line marked in bold. It assigns value b1 to t1. Now t1 refers to the sub-class object. When we invoke the method show(), it executes the method show of sub-class.

 

For C++ Programmers

In Java, there is no keyword virtual. If you study the aforementioned program, you will notice that similar output will occur in C++ program if and only if the show() function in super-class (base class in C++ jargon) is declared virtual.

 

With this much preparation, we are in a position to understand and appreciate polymorphism.

16.3.2 Polymorphism in action

Decades ago, when we used to say library, we could only think of books. Let us take a case of modern library. It will contain not only books but also audiotapes and video CDs. We may say that libraries now store reference material.

Problem: Write a program to demonstrate polymorphism.

Solution: Here we will have one super-class with method show(). Two subclasses will override this method. When we invoke it with a super-class variable, which is pointing to a sub-class, method from that sub-class will be executed (see Program 16.5).

 

PROGRAM 16.5 Polymorphism in Action I

 //     lib1.java

 class refmat

  {String st1 ; // short for reference material

   refmat( String sx )

     { st1 = sx ;

     } ;

   public void show()

     { System.out.println(“method show(): from refmat ”

                        + st1) ;

     };

  } ;

 class book extends refmat

  {int numpage ;

   book( String sy , int num )

     { super (sy);

       numpage = num;

     }

   public void show()

     { System.out.println(“method show(): from book “ );

       System.out.println(“pages = “ + numpage) ;

     }

  } ;

 class cassette extends refmat

     {int time ;

      cassette( String sy , int t )

        { super (sy);

          time = t;

        }

     public void show()

      {System.out.println(“method show(): from cassette “ );

       System.out.println(“time in minutes = “ + time) ;

      }

     } ;

 class lib1

 {   public static void main (String argv[])

     { book b1 = new book( “myJAVA”, 300);

       System.out.println(“---lib1.java---”);

       refmat r1 = b1;

       r1.show();

       cassette ac1 = new cassette(“sound of Music” ,60);

       r1= ac1;

       r1.show();

     }

 };

 

If we run this program, we will get the following output.

 

Output

 ---lib1.java---

 method show(): from book

 pages = 300

 method show(): from cassette

 time in minutes = 60

16.4 Dynamic Method Dispatch

We have just seen that a variable of super-class can refer to a sub-class object.

There may be two or three sub-classes. They may be having method from super-class which is overridden. Now when the compiler sees a method call (see last line in Program 16.5), it may or be able to decide which method to call. Hence, the decision to invoke a method is postponed to runtime. At runtime when such a method call is to be executed, the compiler selects an appropriate method. This behaviour is termed as “dynamic method dispatch”.

Since this behaviour is also seen in C+ +, it will be a good thing to look at similar situations and terms.

When the compiler decides which method to execute, it is termed as static binding. It is termed as compile time polymorphism.

When compiler does not decides which method to execute and leaves the decision to runtime environment (JVM in case of Java), it is termed as dynamic binding or late binding. It is also termed as runtime polymorphism. In other words, dynamic dispatch means run time polymorphism.

There is a subtle difference between Java and C+ +. In C+ +, if the super-class method is declared as virtual, then only the method from sub-class is executed. In Java, there is no keyword virtual. When super-class reference invokes an overridden method, it invokes only the sub-class method.

16.5 Collection of Programs

16.5.1 Sub-class reference cannot be assigned a super-class value

Problem: Write a program to demonstrate that sub-class reference cannot be assigned super-class value.

Solution: See Program 16.6.

 

PROGRAM 16.6 Sub-class Reference Assigned to Super-class I

 //     inher5c.java

 class TOP

        {  String st1 ;

           void init1(String sx )

            { st1 = sx ; }

        } ;

 class BOTTOM extends TOP

         {  String st2;

            void init2 (String sy)

            { st2 = sy ; }

         }

 class inher5c

 {   public static void main (String argv[])

     { TOP t1 = new TOP(); ;

       System.out.println(“---inher5c.java---”);

       BOTTOM b1 = new BOTTOM() ;

       b1.init2(“Jokar”);

       b1.init1(“Hero”);

       b1= t1; // = b1 ;

     };

 }

 

If we run the program, we will get the following compilation error.

 

Output

 Compiling D:javainjavac inher5c.java

 inher5c.java:19: Incompatible type for =. Explicit cast needed to convert TOP to BOTTOM.

 b1= t1; // = b1 ;

   ^

 1 error

 

Hold your breath. If you are feeling that typecasting will solve the problem, you are wrong. The program will get compiled, But when you run it, you will get runtime error. See the following program.

Problem: Write a program to illustrate that attempt of typecasting super-class object into sub-class fails at runtime.

Solution: See Program 16.7.

 

PROGRAM 16.7 Sub-class Reference Assigned to Super-class II

 //     inher5d.java

 class TOP

        { String st1 ;

          void init1(String sx )

           { st1 = sx ; }

        } ;

 class BOTTOM extends TOP

         {  String st2;

            void init2 (String sy)

              { st2 = sy ; }

         }

 class inher5d

 {   public static void main (String argv[])

     { TOP t1 = new TOP(); ;

       System.out.println(“---inher5d.java---”);

       BOTTOM b1 = new BOTTOM() ;

       b1.init2(“Jokar”);

       b1.init1(“Hero”);

       b1= (BOTTOM) t1; // = b1 ;

     };

 }

 

If we run the above program, we will get the following exception (error).

 

Output

 ---inher5d.java---

 Exception in thread main

 java.lang.ClassCastException

 void inher5d.main(java.lang.String[])

 

This is natural. In general, the sub-class object is larger (contains more data) than the super-class object. If super-class object, which has less data, has to transform into sub-class object, it must get additional data. Since this is impossible, language does not permit it.

16.5.2 Arrays of dissimilar elements

In arrays, all the elements (or objects) are required to be of the same type (or class). Arrays are the most powerful data structure. It effortlessly jumps to any element with the help of the index (in other words random access). Some people felt that it would be great if arrays can be made of dissimilar elements. Certain category of problems will be easy to solve (write code).

In Java though we cannot easily have arrays of totally dissimilar elements, we can have arrays of objects belonging to different sub-classes of a particular super-class. This is possible because Java allows us to use super-class reference to point to a sub-class object.

Let us study this with the help of a program.

Problem: Write a program to demonstrate an array of dissimilar elements.

Solution: Let us consider books and cassettes as library material. We will have super-class Libmat extended by sub-classes Book and Cassette. Now we create a single array, add elements to it, and then get them back (see Program 16.8).

 

PROGRAM 16.8 Array of Dissimilar Objects

 //      array1.java

 import java.io.*;

 class LibMat  // short for Library material

   {String title ;

    LibMat( String sx )

      { title = sx ;

      } ;

    public void show()

     {  System.out.println(“method show(): from LibMat “

                        + title) ;

     };

   } ;

 class Book extends LibMat

   {int numpage ;

    Book( String sy , int num )

      { super (sy);

        numpage = num;

      }

    public void show()

      { System.out.println(“ Title : “ + title );

        System.out.println(“ pages = “ + numpage) ;

      }

   } ;

 class Cassette extends LibMat

   { int time ;

     Cassette( String sy , int t )

       { super (sy);

         time = t;

       }

     public void show()

      { System.out.println(“ Cassette ->” + title);

        System.out.println(“ time in minutes = “

                           + time) ;

      }

   } ;

 class array1

 {public static void main (String argv[])

              throws IOException

   {int i ;

    String st1;

    LibMat box[] = new LibMat[3] ;

    BufferedReader Jin = new BufferedReader

                  (new InputStreamReader (System.in) );

    System.out.println(“---array1.java---”);

    box[0] = new Book( “JAVA FAQ”, 300);

    box[1] = new Cassette(“sound of Music” ,60);

    box[2] = new Book( “JAVA Tips and Trics”, 200);

 //       Note box is of class LibMat still we are allowed

 //       to create an object of subclass with it!

    for(i=0;i<3;i++)

       { System.out.println(“Material Index “+i);

         box[i].show() ;

       }

   }

 };

 

If you run the program, you will get the following output.

 

Output

 ---array1.java---

 Material Index 0

 Title : JAVA FAQ

 pages = 300

 Material Index 1

 Cassette ->sound of Music

 time in minutes = 60

 Material Index 2

 Title : JAVA Tips and Trics

 pages = 200

 

At this stage, you may have noticed an important thing. box is of class LibMat. Still we are allowed to create an object of sub-class with it. A constructor constructs an object and returns a reference. This is assigned to a super-class variable. Hence, this is permissible!

RuleBook

super-class

Super-class reference variable can be assigned a sub-class

variable

reference value

sub-class

Sub-class reference variable cannot be assigned a super-class

variable

reference value

multiple

Java does not allow for multiple inheritances, where a class

inheritance

has more than one super-class.

object class

By default, classes inherit from the Object class, which is a super-class of all classes in the Java class hierarchy.

C+ + Corner

In Java, there is no keyword virtual. Consider a case that there is a method in sub-class with same name as the one in super-class. When a super-class reference refers to sub-class object, it always invokes methods in sub-class. This means what a C+ + programmer achieves by declaring a function virtual, a Java program achieves it automatically.

Review Questions

  1. What is polymorphism?
  2. Explain the advantages of super-class reference being assigned a sub-class value.
  3. Explain the terms compile time polymorphism and runtime polymorphism.
  4. Explain dynamic method dispatch with the help of an example.
..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
3.129.42.243