Java inheritance

Inheritance

We will use the simple example most used:

class BaseEmployee {
    String name;
    String address;
    String phoneNumber;
    float yearsExperience;
}

class Manager extends BaseEmployee {
    int teamSize;
    void reportProjectStatus() {}
}

This and Super

this always points to its own instance of itself. Most common example is using this to differentiate instance from local variables:

class Example {

    String value1;             //Instance
    String value2;             //variables

    Example (String value1) {
        this.value1=value1;   //makes clear that the instance variable with the this.
    }


}

class Programmer extends BaseEmployee {
    String[] programmingLanguages;
    void writeCode() {}
}

Here we can see the two different types of employee, Manager and Programmer. Both of these inherit from the BaseEmployee class, and then each of them adds their own methods. These two classes are called subclasses, derived classes, extended classes or child classes, where as BaseEmployee can be referred to Base class, superclass or parent class.

Consider this, a method which accepts a class of BaseEmployee will also accept a subclass of BaseEmployee, meaning you could pass in Manager, for example.

Inheritance allows you to specialise a class by it extending (inheriting) other variables / methods from the parent. This means you are reusing code already defined, rather than re-inventing the wheel.

In the example above, the child class programmer has access to the variables from the parent so you can set the value of name in the child class even though it belongs to the parent class. Oh course there are the access limitations:

Inherited base members:

  • Default access, this means the base and derived class must belong to the same package
  • protected access, this means the derived class can access regardless of package which the base and derived class belong
  • public – well every class can access these
  • Not inherited base members:

  • private access, the derived class does not see these when defined in the base
  • A derived class does not inherit the base class’s constructor – it can call them though
  • Derived classes can also define their own constructor, static methods, variables etc as well as overriding or hiding the base class’s methods / variables etc.

    Abstract base class

    You cannot instantiate abstract classes
    An abstract class does not have to have abstract methods, however an abstract method in a class means it has to be an abstract class
    A derived class extending a abstract base class has to implement all the abstract methods of the base class

    Summarise the rules:

  • A base class / Superclass / Parent class is inherited (extended) by other classes
  • A derived class / subclass / Extended class / child class inherits / extends from a base class
  • Interface

    Interfaces are similar to abstract classes, an interface defines abstract methods (which are all public) and constants.

    Taking the example from before we can apply some interfaces. With the subclasses of Manager and Programmer we can define interfaces which force these classes to implement.

    
    interface Training {
        public void attendTraining();
    }
    
    interface Interview {
        public void conductInterview();
    }
    
    class BaseEmployee {
        String name;
        String address;
        String phoneNumber;
        float yearsExperience;
    }
    
    class Manager extends BaseEmployee implements Interview, Training {
        int teamSize;
        void reportProjectStatus() {}
        public void conductInterview() {                //Have to implement this
               System.out.println("I, manager, am conducting an interview");
        }
        public void attendTraining() {                //Have to implement this
               System.out.println("I, manager, am attending training");
        }
    }
    
    class Programmer extends BaseEmployee implements Training {
        String[] programmingLanguages;
        void writeCode() {}
        public void attendTraining() {                //Have to implement this
               System.out.println("I, programmer, am attending training");
        }
    }
    
    

    NB: the method signatures implemented in the class which is implementing an interface must match. Otherwise this a compile error.

    interface Training {
        public void attendTraining(String[] topics);    //By adding String[] topics, I need to change the implementation
    
    class Manager extends BaseEmployee implements Interview, Training {
        int teamSize;
        void reportProjectStatus() {}
        public void conductInterview() {                //Have to implement this
               System.out.println("I, manager, am conducting an interview");
        }
        public void attendTraining(String[] topics) {                //Have to implement this
               System.out.println("I, manager, am attending training");
        }
    }
    
    class Programmer extends BaseEmployee implements Training {
        String[] programmingLanguages;
        void writeCode() {}
        public void attendTraining(String[] topics) {                //Have to implement this
               System.out.println("I, programmer, am attending training");
        }
    }
    

    A class can only inherit from one class. The reason for this, each class could have the same method but a different meaning, so then which of these methods is inherited?

    
    class Example1 {
         public String greeting() {
             return new Sting("Why hello there");
         }
    }
    
    class Example2 {
         public String greeting() {
             return new Sting("How do");
         }
    }
    
    Class Employee extends Example1, Example2 {
          //Which greeting is called?
    }
    
    

    However, a class can implement many interfaces!

    
    interface ExampleInterface1 {
          String greeting();
    }
    
    interface ExampleInterface2 {
          String greeting();
    }
    
    class Employee implements ExampleInterface1, ExampleInterface2 {
         //Even though we are implementing two interfaces, both define the same method - this is fine as we just need to implement this method
    }
    

    Some rules:

  • A class implements a interface, an interface cannot inherit a class
  • An interface can extend / inherit another interface
  • Abstract class can extend a concrete class, and the other way
  • Abstract class can implement interfaces
  • Abstract class can extend another Abstract class
  • Reference and object types
    class Employee {
    	String name;
    	String address;
    	String phoneNumber;
    	float experience;
    }
    
     interface Training {
     	public void attendTraining();
     }
    
    class Programmer extends Employee implements Training {
    	String[] languages;
    	public void attendTraining() {
    		System.out.println("Programmer attending training");
    	}
    }
    
    class ExampleSpace {
    	public static void main(String[] args) {
    		Programmer p = new Programmer();
    		p.languages = new String[] {"Java", "JavaFX"};
    		
    		System.out.println(p.languages[0]);  //Java
    		
    		p.name = "Test";
    		
    		System.out.println(p.name);  //Test
    		
    		p.attendTraining();
    		
    		//All fine and Dandy, I have created a programmer object and I can access everything.
    		
    		Employee e1 = new Programmer(); //Wait what is this?
    		
    		//e1 is refering to an object of class Programmer, as this extends Employee.
    		
    		/*
    		However, this does not mean it can access everything that Programmer has, e.g.
    		
    			e1.languages = new String[] {"Java", "JavaFX"};
    		
    			System.out.println(e1.languages[0]);
    
    		This will not work as these are specific to the class Programmer. This code will not compile.
    		*/
    
    		//This is fine though, as these belong to the class Employee		
    		e1.name = "Test";
    		
    		System.out.println(e1.name);
    
    		/*
    		Employee does not implement an interface, so this will not work
    		
    			e1.attendTraining();
    		
    		*/
    		
    		//You can also refer to Programmer via the interface!
    		
    		Training t1 = new Programmer();
    		
    		/*
    		Guess what Training cannot do?
    		
    			t1.languages = new String[] {"Java", "JavaFX"};
    		
    			System.out.println(t1.languages[0]);
    			
    			t1.name = "Test";
    		
    			System.out.println(t1.name);
    		
    		It cannot see any of these as it's type is the interface Training. This would not compile.
    		*/
    		
    		//But it can do
    		
    		t1.attendTraining();
    		
    		//All well and good.
    		//Lets just take a look at these:
    		
    		//Programmer p2 = new Employee();  -- this will NOT compile. You cannot refer to a base class using a derived class.
    		//Programmer p3 = new Training(); -- this will NOT compile. You cannot create an object of the type interface.
    	}
    }
    

    You can create an Array of objects, based on a common class, in this case Training which is implemented by Programmer and Manager:

    
    Training[] t1 = new Training[] {
           new Programmer()
           , new Manager()
    };
    
    for (Training tt : t1) {
         tt.attendTraining();
    }
    
    //NB: The base class cannot be added as it does not implement Training, and Training cannot be added.
    
    Casting

    In the example above I place objects of types Programmer and Manager into an array of Training. I can use Casting to reference those objects.

    
    Manager m1 = new Manager();
    m1.teamSize = 5;
    Manager m2 = new Manager();
    m2.teamSize = 15;		
    Training[] t = new Training[] {
    	m1
    	, m2
    	, new Programmer()
    };
    		
    for (Training t1 : t) {
          if (t1 instanceof Manager) {
    	    Manager m = (Manager)t1;  //Casting t1 as a manager as it is of the type manager
    	    if (m.teamSize > 10) {
    		  System.out.println("Why are you on training?");
    		  continue;           //Move on to the next iteration of the loop
    	    }
          }
          t1.attendTraining();
    }
    
    

    This and Super

    this refers to the entity of itself. This is commonly used to distiguish instance variables from method. Example:

    class Example {
         String name;      //Instance
         String address;   //variables
    
         Example(String name) {  
             this.name = name;    //distinguish instance from method variable
         }
    
         Example(String name, String address) {
             this(name);                          //Calls the constructor which only takes name
             this.address = address;              //sets address
         }
    
    }
    

    Another example of using this to call the “default” constructor.

    class Example {
         String name;      //Instance
         String address;   //variables
         String postcode;
    
         Example() {                            	//Constructor which does not take any arguments
              name = "No data";
              address = "No data";
              postcode = "No date";
         }
    
    
         Example(String name, String address, String postcode) {
              this();				//This must be the first line of the method
              if (name != null) this.name = name;
              if (address != null) this.address = address;
              if (postcode != null) this.postcode = postcode;
              
         }
              
    }
    

    this can also be used to access inherited members of a base class in the derived class.

    Super

    This is normally used when a derived class define methods already present in the base class.

    class Employee {
    	String name;
    }
    
    class Programmer extends Employee {
    	String name;
    	
    	void setNames() {
    		this.name = "Programmer";
    		super.name = "Employee";
    	}
    	
    	void printNames() {
    		System.out.println(this.name);
    		SYstem.out.println(super.name);
    	}
    }
    

    You can also refer to constructors with super:

    class Employee {
    	String name;
    	Employee(String name) {
    		this.name = name;
    	}
    }
    
    class Programmer extends Employee {
    	String[] languages;
    	
    	Programmer(String name, String[] languages) {
    		super(name);                    //As with the this example, this call to the constructor must be the first line of code.
    		this.languages = languages;
    	}	
    }
    

    You can’t use this and super in static methods, the code will fail to compile!

    Polymorphism

    It means of many forms, think of a method e.g. talk, which depending on the object it belongs to you get a “different” result, but nevertheless the result is talking.

    abstract class Employee {
    	String name;
    	
    	public void reachOffice() {
    		System.out.println("Reached the office");
    	}
    	
    	public abstract void startProjectWork();  //Here we have defined a method which has to be implemented when employee is extended. Each object will deal with this in thier own way.
    }
    
    class Programmer extends Employee {
    	public void startProjectWork() { //This is also referred to as a overridden method
    		writeCode();
    		writeUnitTests();
    	}
    
    	private void writeCode() {
    		System.out.println("writing code");
    	}
    	
    	private void writeUnitTests() {
    		System.out.println("writing unit tests");
    	}
    }
    
    class Manager extends Employee {
    	public void startProjectWork() { //Have to implement this as I have extended Employee!
    		meetClients();
    		defineProject();
    		assignTeam();
    	}
    	
    	private void meetClients() {
    		System.out.println("Meeting with clients");
    	}
    	
    	private void defineProject() {
    		System.out.println("defining project");
    	}
    	
    	private void assignTeam() {
    		System.out.println("assigning team");
    	}
    }
    
    class PolyMorphism {
    	public static void main(String[] args) {
    		Employee e1 = new Programmer();   //creates object of Programmer and assigns to Employee
    		Employee e2 = new Manager();	//create object of Manager and assigns to Employee
    		
    		e1.reachOffice();  //This is defined in the Employee class
    		e2.reachOffice();
    		
    		e1.startProjectWork();  //Method from the programmer
    		e2.startProjectWork();  //Method from the manager
    	}
    }
    

    Note above, the reference variables of type Employee when the method startProjectWork is called they behave in accordance to the classes defined. startProjectWork is polymorphic in the same way talk is.

    The method in the abstract class means you have to implement it, in doing so you are actually overriding it. This method can be either abstract or not, and the access modifiers can be the same or less.

    NB: This is overridden methods – this is a method with the same name, number and type of arguments. Do not confuse with overloaded methods which accept a different number or type of parameters.

    The difference between variables bound at compile time and runtime:

    class Employee {
    	String name = "employee";
    
    	void printName() {
    		System.out.println(name);
    	}
    }
    
    class Programmer extends Employee {
    	String name = "programmer";
    
    	void printName() {
    		System.out.println(name);
    	}
    }
    
    class BindingExample {
    	public static void main(String[] args) {
    		Employee emp = new Employee();
    		Employee programmer = new Programmer();
    		
    		System.out.println(emp.name);         //prints employee
    		System.out.println(programmer.name);  //prints employee - Because the type of the variable is employee - this is bound at compile time
    		emp.printName();			//prints employee - accesses method within Employee
    		programmer.printName();			//prints programmer - access overridden method in Programmer
    	}
    }
    
    Interfaces

    Lets implement Polymorphism with interfaces, if we stay with the same objects as before (Employee, Programmer, and Manager) we shall implement finishWork().

    
    interface Project {
    	void finishWork();
    }
    
    class Programmer extends Employee implements Project {
    	public void finishWork() {
    		System.out.println("Coding complete");
    		System.out.println("unit tests complete");
    	}
    }
    
    class Manager extends Employee implements Project {
    	public void finishWork() {
    		System.out.println("QA complete");
    		System.out.println("Documentation done");
    	}
    }
    
    class PolyMorphWithInterfaces {
    	public static void main(String[] args) {
    		Project p1 = new Programmer();   //Because programmer and manager implement Project
    		Project p2 = new Manager();	//then the type of the variable can be Project
    		//If you stored these objects as type Employee you would not be able to call this method
    		
    		p1.finishWork() //returns "Coding complete" and "unit tests complete"
    		p2.finishWork() //returns "QA complete" and "Documentation done"
    	}
    }
    

    All fine and Dandy. A little disappointing that you have to define the type as Project, this limits us somewhat. We can address this by having Employee implement Project which, because Programmer and Manager extend Employee, means Employee types can call finishWork().

    interface Project {
    	void finishWork();
    }
    
    class Employee implements Project {
    	public void finishWork() {
    		System.out.println("Employee is done");
    	}
    }
    
    class Programmer extends Employee {
    	public void finishWork() {
    		System.out.println("Coding complete");
    		System.out.println("unit tests complete");
    	}
    }
    
    class Manager extends Employee {
    	public void finishWork() {
    		System.out.println("QA complete");
    		System.out.println("Documentation done");
    	}
    }
    
    class PolyMorphWithInterfaces {
    	public static void main(String[] args) {
    		Employee p1 = new Programmer();   
    		Employee p2 = new Manager();	
    		
    		p1.finishWork() //returns "Coding complete" and "unit tests complete"
    		p2.finishWork() //returns "QA complete" and "Documentation done"
    	}
    }
    

    Nice and clean…

    Remember, Polymorphism is overridden methods, not overloaded.

    reference: http://www.manning.com/gupta/

    Advertisements

    Tags: , , ,

    Leave a Reply

    Fill in your details below or click an icon to log in:

    WordPress.com Logo

    You are commenting using your WordPress.com account. Log Out / Change )

    Twitter picture

    You are commenting using your Twitter account. Log Out / Change )

    Facebook photo

    You are commenting using your Facebook account. Log Out / Change )

    Google+ photo

    You are commenting using your Google+ account. Log Out / Change )

    Connecting to %s


    %d bloggers like this: