Inheritance

Inheritance

Every class that you create in Java is a subclass, its ultimate parent is the Object class that every class inherits even if you dont declare it. To inherit a class we use the extends keyword the extended class is known as the parent or base class or superclass, the object performing the extending is known as the child (derived) or subclass

There are three types of inheritance:

A subclass is said to be of type superclass and an instance of the subclass can be used anywhere an instance of the superclass can be used basically subclass 'IS A' of the superclass

There are a few rules regarding IS-A and HAS-A relationships

IS-A
(inheritance)

In OO, the concept of IS-A is based on inheritance, IS-A is a way of saying "this thing is a type of that thing", for example a dog is a type of animal so a dog IS-A animal, bentley IS-A car, apple IS-A fruit, etc

This is expressed in Java through the keyword extends
   public class Vehicle { ... }
   public class Car extends Vehicle { ... }
   public class Ka extends Car { ... }

So we can say that "Ka IS-A car" and "Ka IS-A Vehicle" because a class is said to be "a type of" anything further up in its inheritance tree.

When using the extends keyword a class inherits all accessible member variables and methods

public class TestAnimal {
   public void static main (String[ ] args) {
      Horse h = new Horse();

      h.getEat();                              // getEat() method inherited from Animal
      h.food = "Hay and Oats";           // change the inherited (from Animal) food variable

     System.out.println("I like to eat " + h.food);
}

class Animal {
   public String food = "Green Stuff";

   public void getEat() {
        System.out.println("General food from the Animal class");
   }
}

class Horse extends Animal {
    // Will inherit Animals getEat() method and food member variable
}

HAS-A
(usage)

HAS-A relationships are based on usage, rather than inheritance, in other words class A HAS-A B if code in class A has a reference to an instance of class B for example "city HAS-A road".

The below Horse class has an instance variable of type Halter, so you can say that "Horse HAS-A Halter" (Horse has a reference to Halter). This means that you can invoke any of the methods in Halter, this is good OO practice as we do not need to duplicate the methods in Halter and this means that any other class can use Halter methods thus reducing code.

public class Animal { ... }
public class Horse extends Animal {
   private Halter myHalter;

   public void tie(LeadRope rope) {
      myHalter.tie(rope);              // delegate tie behavior to Halter Object
   }
}

Users of the Horse class think that the Horse class has Halter behavior, the Horse class may have a method called tieRope but this method could delegate this to the Halter tieRope method (as seen above), users of Horse class should never know that there is a Halter, only that is capable of things you ask it to do.


There are visual object modelling languages such has the Unified Modeling Language (UML) which allow designers to design and easily modify classes without having to write code first because object-oriented components are represented graphically. This helps coders to create a map of the class relationships and helps them recognize errors before coding begins. There are many design patterns available which cover a lot of different types of projects.

To obtain what class an object belongs to you can use the following

what class an object belongs to System.out.println("The class of object " + obj + " is " + obj.getClass().getName() );

A class consists of two types of elements:

The rules for inheritance is as follows:

Create and Extend Abstract Classes

An abstract class is a class that is declared with the abstract modifier and it may or may not include abstract methods. An abstract class defers some or all of its implementation to its subclass and thus an abstract class cannot be instantiated. An abstract class is use to define common attributes and behavior for a class that will extend it.

The class example of an abstract class is the Animal class which is extended by the Dog class, as you normally will not instantiate an Animal class, the Animal class will contain common attributes and behavior that can be used by several animals, for example walk, bite, number of legs, etc. A class that is not abstract is called a concrete class. Note to remember is that an abstract class needs a subclass to implement its methods, thereby forcing commonality of interface. If one method is declared abstract then the class itself needs to be abstract as well, fields cannot be declared abstract.

Abstract class rules are as follows:

Minimal abstract class
abstract class AbstractClass {}
abstract class
public abstract class Vehicle {

    // We create an enum which describes possible subclasses
    protected enum VehicleType {
        Automobile, Motorcycle, Moped, Bicycle, Scooter
    }

    // These attributes will be common to all subclasses
    private VehicleType type;
    private String owner;
    private String make;

    // Standard Constructor
    public Vehicle(VehicleType type, String owner, String make) {
        this.type = type;
        this.owner = owner;
        this.make = make;
    }

    // toString method
    public String toString() {
        return "Vehicle{" +
                "type=" + type +
                ", owner='" + owner + '\'' +
                ", make='" + make + '\'' +
                '}';
    }

    // As the below are all abstract they will need to be implemented by the subclass
    public abstract void drive();

    public abstract void park();

    public abstract void makeNoise();
}
    
// Vehicle is a concrete class which extends the abstract class Vehicle
public class Auto extends Vehicle {

    // Create a constructor that is a pass thru to Vehicle constructor
    public Auto(VehicleType type, String owner, String make) {
        super(type, owner, make);
    }

    // Implement Vehicle's drive method
    public void drive() {
        System.out.println("Release Brake, go");
    }

    // Implement Vehicle's park method
    public void park() {
        System.out.println("Parallel or back in?");
    }

    // Implement Vehicle's makeNoise method
    public void makeNoise() {
        System.out.println("Vroom Vroom");
    }

    // Main method creates an instance of Auto and demonstrates calling the
    // concrete methods
    public static void main(String[] args) {
        Auto car = new Auto(VehicleType.Automobile, "Allen", "Ford");
        System.out.println(car);
        car.drive();
        car.park();
        car.makeNoise();
    }
}

I have not covered Interfaces yet but here is a table of the differences between abstract class and an interface. If you want to provide common, implemented functionality among all implementations of your component, use an abstract class. Abstract classes allow you to partially implement your class, whereas interfaces contain no implementation for any members.

Abstract class Interface
Abstract class can have abstract and non-abstract methods. Interface can have only abstract methods. Since Java 8, it can have default and static methods also.
Abstract class can define public, protected, package-private or private concrete methods All abstract methods are public
An abstract method must be declared abstract If a method has no body by default its implicity abstract
Abstract class doesn't support multiple inheritance. Interface supports multiple inheritance.
Abstract class can have final, non-final, static and non-static variables. Interface has only public, static and final variables.
Abstract class can provide the implementation of interface. Interface can't provide the implementation of abstract class.
The abstract keyword is used to declare abstract class. The interface keyword is used to declare interface.
An abstract class can extend another Java class and implement multiple Java interfaces. An interface can extend another Java interface only.
An abstract class can be extended using keyword "extends". An interface can be implemented using keyword "implements".
A Java abstract class can have class members like private, protected, etc. Members of a Java interface are public by default.

Using super

Whenever a subclass needs to refer to it's immediate superclass it can do so by using the keyword super. super has two general forms, the first calls the superclass constructor, the second is to access a member of the superclass that has been hidden by a member of a subclass.

Super example
public class SuperTest extends SuperClass {

    int i = 10;                                                       // hides the superclass variable i

    public SuperTest() {
        super();                                                      // we call the superclasses constructor
    }

    public static void main(String[] args) {
        SuperTest st = new SuperTest();
        st.getSuperVariable();
    }

    public void getSuperVariable() {
        System.out.println("local variable i is : " + i);             // getting the local variable i
        System.out.println("superclass variable i is : " + super.i);  // getting the superclasses variable i
    }
}

class SuperClass {

    int i = 5;                                                        // this gets hidden by the local variable i

    public SuperClass() {
        System.out.println("This is the super constructor");
    }
}

Object Class

There is a special class in Java called the Object Class, all other classes are subclasses of Object, its a superclass of all other classes. Object defines a number of mewthod that all classes inherit or can override.

Object clone() Creates a new object that is the same as the object being cloned
boolean equals(Object object) Determines whether one object is equal to another
void finalize() Called before an unused object is recycled
Class<?> getClass() Obtains the class of an object at runtime
int HashCode() Returns the hash code assciated with the invoking object
void notify() Resume execution of a Thread waiting on the invoking object
void notifyAll() Resumes execution of all Threads waiting on the invoking obect
String toString() Returns a string that describes the object.
void wait()
void wait(long milliseconds)
void wait(long milliseconds, int nanoseconds
Waits on another thread of execution
toString() method

When you want a to read something more meaningful about an object of your own class you can override the toString() method.

overriding toString() example

public class myTest() {
   String classInfo = "This is my new class that overrides the toString method";

   public String toString() {
      return ("This is some information about myTest class" + classInfo);
   }
}

public class Test() {
   public static void main(String[ ] args) {
      myTest m1 = new myTest();

      System.out.println(m1);
      System.out.println(m1.toString());
   }
}

Note: we can put lots more information in the returning String if we wish

Overriding equals()

There are cases when you want to compare two objects that contain the same attributes and because the equals() method in the Object class uses only the = = operator for comparison, the two objects are only considered equal if the references refer to the same object. This method for comparing has a problem when using Hashtables, lets say we have a number of car objects with the same attributes and you put these into a Hashtable, when you search the Hashtable to get a number of objects with the same attributes, you will only retrieve the one which matches the reference you have, this is because the equals() method only uses the = = operator and hence only one object will match within the Hashtable

Not overriding equals()

public class stringTest {
   public static void main(String[] args) {

      Moof one = new Moof(8);
      Moof two = new Moof(8);

      if ( one.equals(two) ) {
         System.out.println("One equals two");
      } else {
         System.out.println("One does not equal Two");   // Will be outputted, they are not equal
      }
   }
}

class Moof {
   private int moofValue;

   // Constructor
   Moof(int val) {
      moofValue = val;
   }

   // Method
   public int getMoofValue() {
      return moofValue;
   }
}

Note: even though objects one and two are the same (same attributes) they are not equal because we use the Object class equals which uses the == operator to compare and as objects one and two do not point to the same reference they are not considered equal

overriding equals() public class stringTest {
   public static void main(String[] args) {

   Moof one = new Moof(8);
   Moof two = new Moof(8);

   if ( one.equals(two) ) {
      System.out.println("One equals two");    // using the overridden equals objects are equal
   } else {
      System.out.println("One does not equal Two");
   }
}

class Moof {
   private int moofValue;

   // Constructor
   Moof(int val) {
      moofValue = val;
   }

   // Method
   public int getMoofValue() {
      return moofValue;
   }

   // equals override, so that we can match objects which have the same attributes
   public boolean equals(Object o) {
      if ((o instanceof Moof) && (((Moof)o).getMoofValue() == this.moofValue)) {
         return true;
      } else {
         return false;
      }
   }
}

Note: now we override equals so that the objects are now equal

The equals() method has a contract which has a number of rules that must be followed (obtained from the Java docs)

equals() and hashCode() are bound together by a joint contract, that states two equal objects should have the same hashcode as well, so to be very safe if you override the equals() method then you should override the hashCode() method.

Overriding hashCode()

The hashcode value of an object is used in collection classes, it's basically a object ID number, however it is not unique. The hashcode is used to store the object in collections such as HashMap and HashSet, this code is also used to locate the object as well.

hash code example String s1 = "Hello";
String s2 = "hello";

System.out.println("The hashcode for " + s1 + " is " + s1.hashCode() );   // results in 69609650
System.out.println("The hashcode for " + s2 + " is " + s2.hashCode() );   // results in 99162322

How do hashcode's work, is that the collection will use a number of buckets that will contain hashcode's that point to data. For example lets say we have four people objects, each object is hashed by using an hashing algorithm and then placed into a bucket

  Key HashCode Algorithm HashCode
  Alex A(1) + L(12) + E(5) + x(24) = 42
  Bob B(2) + o(15) + b(2) = 19
  Dirk D(4) + i(9) + r(18) + k(11) = 42
  Fred F(6) + r(18) + e(5) + d(4) = 33

In real life the hash algorithm is a lot more advanced than this, as we can see Alex and Dirk will go into one bucket, Bob in another and Fred in another, so we have a bucket that contains two people objects which is acceptable. So now when you perform a search you no longer need to check each object, you simple hash the object you want to search for then go straight to that bucket and then only search through that bucket cutting down the number of objects to check against thus speeding up processing., in this case if you searched for Alex we would go straight to bucket 42 then search the two objects in there for Alex (thus only two searches instead of 4).

Now the problems arise if you do not pass the right name to search for (missing letter, upper case instead of lower case, thus the hashcode produced for you search would not match any bucket, now you see why if two objects are considered equal, their hashcode's must also be equal, Otherwise you'd never be able to find the object since the default hashcode method in class Object virtually always comes up with a unique number for each object. so if two objects are equal their hashcode's must be equal as well.

overriding hashcode class HasHash {
   public int x;
   HasHash(int xVal) {
      x = xVal;
   }

   // Override the equals method   
   public boolean equals(Object o) {
      HasHash h = (HasHash) o;      // don't try without instanceof test, use the code from the example above

      if ( h.x == this.x) {
         return true;
      } else {
         return false;
      }
   }

   // Override the hashCode method
   public int hashCode() {
      return (x * 17);             // does not need to complicated - see below
      // return 1234;              // could even just return the same value to make objects equal
   }
}

What we are trying to achieve is a even distribution of data across the buckets, thus speeding up searches. Again the hashCode contract states the following rules

In short this means

Condition Required Not required (but allowed)
x.equals(y) == true x.hashCode() == y.hashCode()  
x.hashCode() == y.hashCode()   x.equals(y) == true
x.equals(y) == false   no hashCode() requirement
x.hashCode() != y.hashCode() x.equals(y) == false  

Using instanceof

Sometimes you need to know what type an object is during runtime, for example you could have one thread creating objects and another thread processing them, the processing thread can then check th type of object to confirm that it can process it, also you can check before you cast an object to make sure it can be casted (otherwise you could get errors). Java uses the instanceof operator to confirm the type of an object.

instanceof example
class A {
    int i, j;
}
class B {
    int i, j;
}
class C extends A {
    int k;
}
class D extends A {
    int k;
}
class InstanceofTest {
    public static void main(String args[]) {
        A a = new A();
        B b = new B();
        C c = new C();
        D d = new D();
        
        if(a instanceof A)
            System.out.println("a is instance of A");
        if(b instanceof B)
            System.out.println("b is instance of B");
        if(c instanceof C)
            System.out.println("c is instance of C");
        if(c instanceof A)
            System.out.println("c can be cast to A");
        if(a instanceof C)
            System.out.println("a can be cast to C");
        System.out.println();
        
        // compare types of derived types
        A ob;
        ob = d; // A reference to d
        System.out.println("ob now refers to d");
        
        if(ob instanceof D)
            System.out.println("ob is instance of D");
        System.out.println();
        
        ob = c; // A reference to c
        System.out.println("ob now refers to c");
        if(ob instanceof D)
            System.out.println("ob can be cast to D");
        else
            System.out.println("ob cannot be cast to D");
        
        if(ob instanceof A)
            System.out.println("ob can be cast to A");
        System.out.println();
        
        // all objects can be cast to Object
        if(a instanceof Object)
            System.out.println("a may be cast to Object");
    }
}

Static Import

Using static import will import all static members of a class or interface, you can then access directly using their names with qualifying the with their class name.

static import example
// Use static import to bring sqrt() and pow() into view. 
import static java.lang.Math.sqrt;             // you can use a wildcard here to import all static members
import static java.lang.Math.pow; 
 
// Compute the hypotenuse of a right triangle. 
class Hypot { 
  public static void main(String args[]) { 
    double side1, side2; 
    double hypot; 
 
    side1 = 3.0; 
    side2 = 4.0; 
 
    // Here, sqrt() and pow() can be called by themselves, without their class name. 
    hypot = sqrt(pow(side1, 2) + pow(side2, 2)); 
 
    System.out.println("Given sides of lengths " + 
                       side1 + " and " + side2 + 
                       " the hypotenuse is " + 
                       hypot); 
  } 
}