More OO                                                                            Peter Komisar
                                                                                                                                             minor revision May 15/ 2001

references: 'Just Java'  P.V.Linden, 'Java Certification Study Guide', Heller&Roberts


Inheritance

Inheritence may be described variously as 1) extending 2) adopting 3) adapting or 4) building
on a pre-defined parent class. In Java, if a parent is not explicitly mentioned in the class definition
(after the extends keyword), the parent is Object implicitly. The extends keyword, is followed
in a class definition by the parent or superclass from which the new class is being defined
as inheriting from. Non-private, parent members are inherited by the subclass as if they were
declared in the subclass. In Object-Oriented Programming, hierarchies of classes are created
by class extensions. The hierarchies increase in specialization and complexity proceeding from
top to bottom.



How to inherit

If you want a bilingual button, in java you don't have to re-invent the button. In object-oriented
languages like java, you can inherit the attributes of an existing button and then go on to add
your own features. The class extends the the parent class to become the subclass.

A subclass is created using the extends keyword after the class name and before the name
of the class that is being extended.

Example     class Child extends Parent {   }
 
Subclassing JButton


/* by subclassing JButton, a custom button is obtained with a few lines of code without 
     needing to rewrite or copy the hundreds of lines of code needed to build a functioning
     activatable visual component from scratch */

 import javax.swing.*;

 class BiTextButton extends JButton{
       BiTextButton(String english, String french){
         setText(english + " / " + french); 
         }
 }
 // This is just a bit of code to run and show the new button 

 class bT{
 public static void main(String[] args){
   JFrame jf=new JFrame();
   jf.getContentPane().add(new BiTextButton("dog","chien"));
   jf.setSize(400,300);
   jf.setVisible(true);
   }
 }

The inheritance, achieved by subclassing, saves on copying, modifying and maintaining
code as well as ensuring compatibility with future versions. Inheritance provides one of
the key objectives of object-oriented programming which is reusability. Inheritance provides
more than a template for child classes. The parent class members may be accessed by child
classes. So if a parent has a method, a child class can invoke it as if it was it's own even though
it doesn't appear in the child's class definition. For example, in the code above we call the
setText( ) method we inherit from the JButton class though we haven't defined it in our child
version. By the same token JButton has parent classes and those parent's members and
methods are also available for use in JButton and in our grandchild class. There are a few
restrictions. Though descendants, subclasses cannot access parent members marked private.

private carKeys; // a private variable  in the parent class will not be accessible from the child class

A class may use member which it has inherited from anywhere in it's parental hierarchy, all the
way back to Object  (This has practical implications when trying to trace a class member.When
looking in the API for say, a method or a static variable, if you don't find it in the immediate
parent class, you may have to look higher up the hierarchy to locate it.) The API lists inherited
features. // Check out a class like JButton in the API and notice the lists of inherited methods etc.
 
 More on Constructors          // for reference

 /* Constructor Note: If you don't explicitly call the superclass constructor, the superclass 
  no-args  constructor is provided for you. If the superclass has no no-args constructor as a 
  result of other constructors having been defined this automatic no-args constructor is not
  supplied. If you try to invoke it, it's not there and the compiler will report an error. super
 can be used to invoke the fields of any superclass. Run the following code, then comment 
 out the no-args Bird constructor in the  parent class. You'll get a compiler error indicating 
 the absence of the no-args constructor the child class is trying to invoke. To summarize, 
 if you define constructors, the automatic no-args constructor is not supplied.  */

 class Bird{       // the parent class with two constructors
     Bird( ){
       System.out.println("The Sounds of Silence");
        }
   Bird(String song){
       System.out.println (song);
       }
    }

 class Parrot extends Bird{     // a child class invoking the two parent constructors
     Parrot(){
     super();
     }
  Parrot(String s){
     super(s);
     }
   } 

 // constructing the parent and child via the childs two constructor forms 

 class BirdBath{ 
   public static void main(String[]args){
    new Bird();
    new Parrot();
    new Parrot("More like a squack");
   }
 }


" Is a " vs. " Has a "

To distinguish when a component should be a class member vs. an extension of the
inheritance relationship, the " is a " / " has a " test can be used.

Example 1  You have a car class and an ID class :  A car has an ID OR A car is an ID
                Has a is right so let ID be a member of the car class.

Example 2   You have a mammal and dog class: A dog has a mammal OR a dog is a
                 mammal A dog is a mammal so dog class should extend mammal.


Java doesn't multiple inherit

The dilemna -->  If B & C descend from A, and D descends from B & C, given identical
                               variables, which does D inherit, B's or C's

The solution --> The java interface type provides inheritance from several sources
                               without the complexity associated with multiple inheritance.


Variable Hiding

When a child class extends a parent class and then explicitly defines identical variable to
those found in the parent, the child variables are said to hide or mask the values of the
inherited parent variable. The parent values have still been inherited. You just have to
take extra steps to expose these values. The following code example will demonstrate this.
 
 
Code showing variable hiding of a parent class variables by those of the child 

/** Same name data referencing in parent and child classes **/

  // parent class
 class Parent{ 
       String n ="Mom";
       }

 // child class
 class Child extends Parent{ 
       String n = "Tom";
       String pn=super.n;
       }

  // test class with main (save source file to identifiier, pcT.java)
 class pcT{ 
 public static void main(String args[] ){
    Parent p=new Parent(); 
    Child c=new Child( );

 /* String s shows parent and child, n, variable values of same name, as 
      well as a cast of child to parent type which exposes the parent value 
      of the variable n. Alternatively variable pn also shows how a call to 
      super can unmask the parents version of the variable.
  */
    String s =        // comment parts are muted and variables highlighted
      "Parent's n is " + p.n            +"\n"+ 
      "Child's n is "   + c.n            + 
      " which masks the parent class  n value. \n"+
      "Casting the child instance, c, to Parent type yields Parent's n value, " + 
       ((Parent)c).n + "."               +"\n"+ 
      "Using super also exposes the parents value of n, " + c.pn + ".";

    //  String s is sent to console 
        System.out.println(s); 
    }
 }

 OUTPUT
 
.
C:\NewCode>java pcT

Parent's n is Mom.
Child's n is Tom, which masks the parent, n value. 
Casting the child instance, c, to Parent type yields Parent's n value, Mom.
Using super also exposes the parents value of n, Mom.
..


Superclass/Subclass compatibility

A subclass object can be used anywhere a more general superclass object can be used, so in
OO programming, a variable of a class may actually be referring to a more specialized subclass

Example  Specialized_subclass  s = new Specialized_subclass( );
             General_superclass  p = s;  // the subclass is being assigned to the type of the superclass
 



A Note on Casting Primitive Types

We saw in an earlier example casting between primitive types. For the following code shows
an example of this, and in passing, what happens when a cast is made that cannot be correctly
stored in the variable declared. In this case a meaningless number is stored.

class caster{
public static void main(String[]args){
long l=40000000000L;

// interesting if the explicit designation L is not included the value is assumee to be an int
// despite it about to be assigned to a long variable. The compiler reports the value is too
// too big to be stored as an int. The L resolves this.

System.out.println("l as a long "+ l );
int i=(int)l;
System.out.println("l's value, too big for an int, becomes " + i );

// This results in a meaningless number unless the assigned
// number is actually in the range of the type declared.
}
}

Casting Reference Types

The rules governing casting reference types may be summarized as follows.

1) a more general object can always hold a more specialized one (parent = child)
2) reverse the process by casting (child =  (child) parent ) // compiles ok but may be ok at runtime?
3) At runtime the cast must be legal or a ClassCastException occurs*
4) You cannot cross cast between unrelated classes
5) Every class inherits from, therefore can be cast to Object (i.e. Vector stores Object)
    * Exception: a recoverable interuption to normal flow of control

// reminder: instanceof checks if a reference (on the left-hand side), is an instance, (class or sub-class),
of a given type (on the right-hand side).

Casting Example

                 public class FlyingMachine{ . . . }
                 public class Jet extends FlyingMachine{. . .}
                 public class Balloon extends FlyingMachine {. . .}
                 FlyingMachine  fm;                 // a reference to the parent
                 Jet F16=new Jet( );                         // a child of one sub-type
                 Balloon zepellin = new Balloon( );          // a child of another sub-type
 /*1*/        fm = F16;             // OK,  a child can always be assigned to parent type
 /*2*/        F16 = zeppellin;   // Won't compile,  you can't cross assign unrelated classes
 /*3*/        F16 = fm;      // Won't compile, you can't assign down the inheritance tree
                 F16 = (Jet)fm;      // BUT with a cast a parent can be assigned to child however
                                             // this may fail at runtime as the check will be made to see if
                                             // assignment is to the same type or that of parent
 

Quick Inheritance Assignment Summary

                  P (parent)                    //      1) Assigning up is OK , child to parent  P = A;
           ____|____                        //      2) Across is not                       A =/=B;
           |                |                          //      3) Can't assign down, A=/=P, but can cast down, A=(A)P;
          A (child)   B (child)             //           though at runtime it be checked to see if it's legal

//   At runtime the expression must resolve to an assignment to the same type or to the parent type
//   or a runtime error occurs


Polymorphism

Polymorphism means many forms or shapes and is used in object oriented programming
to refers to how different behaviours are associated with a same method name. In java, this
is achieved by the techniques of overloading and overrriding.

Overloading is considered shallow polymorphism and is resolved by the compiler. Methods
and constructors can be overloaded. Overriding is deep polymorhism and is resolved at
dynamically at runtime.

Method Overloading

When methods with the same names are distinguished by variations in their argument lists they
are said to be overloaded.. The compiler distinguishes same-name methods by variations in the
1) type 2) order & 3) number of parameters in the method's signature. Return type and
exceptions thrown, are not of consequence in overloading.

Examples   // all are different methods which are overloaded in that they have the same name

     public void measure(int i){ }
     public void measure(int i, int j){ }        // distinguish by # of parameters
     public void measure(int i, short j){ }    //   "     "           type    "      "
     public void measure(short j, int i){ } //   "     "           order  "      "

/* adding
    public String measure(short z, int x) throws Exception{ //stuff }
    would not be distinguishable  from the previous method example.
   The identifier isn't relevent, only the type
*/

Another good example of overloading is provided in the PrintWriter class with several it's
several print( ) methods, one for each primitive type as well as Object class and String.

// i.e. void print(boolean b), void print(char c), void print(double d) void print(float f),  etc.

Method Overriding

Overriding describes the case where a subclass method has an identical signature to that
of a method in the parent class, ( the same method name and number, type and order of
parameters) The subclass method effectively overrides the superclass' version of the method.
The rules governing overriding are as follows.

1) return types in overridden methods must match
2) Overridden methods cannot throw new exceptions or exceptions that are more
    general than those of the parent method.
3) access modification cannot be more private than in parent method only more public.
 
 
 
 An example of subclasses overriding the parent method


  class Pop{
     // a class with a method as member
     String getCoffee( ){
     String black = "Black thanks";
     return black;
    }
  }
  class Tot extends Pop{
    // overrides getCoffee
    String getCoffee( ){
    String noThanks = "No thanks";
    return noThanks;
    }
  }
  class Dot extends Pop{
    // also overrides getCoffee
    String getCoffee( ){
    String sugar = "Sugar please";
    return sugar;
    }
  }
  class Host {
    public static void main(String[] args){

    // We will declare them all to the general parent type to 
    // show the parent type can act as type for subclasses

    Pop pop,tot,dot;
    pop = new Pop( );
    tot = new Tot( );
    dot = new Dot( );

    // Here's polymorphism. The parent and subclasses have 
    // the same operations called but all behaves differently 

    System.out.println("How do you like your coffee?");
    System.out.println("Pop? " + pop.getCoffee());
    System.out.println("Tot? " + tot.getCoffee());
    System.out.println("Dot? " + dot.getCoffee());
    }
  }

At runtime, the correct method is chosen based on what object is actually being referenced.
This allows a class to implement an operation or behaviour which is unique to itself. ( Methods
with the same name in both a superclass and a subclass are called according to the runtime
determination of what class is actually being referenced, this accomplished by class Class
using RTTI, Run Time Type Information )
 
 
 More on class Class 


 Every class file has an object of type Class stored in it, a tag which the compiler first calls to get 
 the 'blueprints' for the class it must build from the array of bytes stored in the class file. Class 
 stores vital type information for checking casts at runtime, when classes are dynamically  loaded 
 as needed. Class objects may also be used to provide information about each or the different 
 members of a given instance. The information class Class provides is vital to allow the operation 
 of distributed, object-oriented applications to work over networks, assisting java's class loaders
 to load class objects remotely, as they are called from the java runtime environment ( JRE ).
.

// Notes: Fields and methods can have same names but it isn't a recommended  practice. Speaking of style,
// 'The Java Language Specification' states method names should be based on verbs and fields on nouns.
//  A group of ovrerloaded methods would normally be designed to be of related purpose


The final keyword applied to classes

When a class is marked final it becomes the last in an inheritance line and cannot be subclassed.
The Math class is an example of a final class. All of Math's methods are static. Math is made
final to improve performance and to ensure consistent results. The final keyword signals the
compiler to preload the class rather than leaving it to runtime to decide which overridden method
to use. final is also used for security reasons.

String class is also a final class. Making String final prevents the changing of read-only attributes
in subclasses, as might be possible the String class' version of toString( ) could be overridden.
( toString( ) is one of the methods defined in class Object which all classes inherit )


The abstract keyword applied to classes

In a sense final and abstract are opposites. final prevents extending and overriding while
abstract forces overriding to take place. An abstract class cannot be instantiated. To be
used it must be extended. abstract at the beginning of a class means zero or more of it's
methods are abstract. A good example of a common abstract class is the Component class.
It is root to all the visual component classes.
 
Reminder While we are here, an abstract method has no body. It is incomplete and requires an implementation to be used. And the rule is, If a method is marked abstract, the class containing 
it must also be marked abstract.