Object-Orientation

Object Orientation

Object Orientation is programming design concept. Put simply, it is a way of writing classes that interact with each other in a more logical way. It therefore, enables far better code reuse, and better code readability. It involves three principles, Inheritance, Encapsulation and Polymorphism. For a programming language to be considered object orientated it must support these three principles.

Inheritance

Java Inheritance defines an is-a relationship between a superclass and its subclasses. This means that an object of a subclass can be used wherever an object of the superclass can be used. Class Inheritance in java mechanism is used to build new classes from existing classes. The inheritance relationship is transitive: if class x extends class y, then a class z, which extends class x, will also inherit from class y.

For example a car class can inherit some properties from a General vehicle class. Here we find that the base class is the vehicle class and the subclass is the more specific car class. A subclass must use the extends clause to derive from a super class which must be written in the header of the subclass definition. The subclass inherits members of the superclass and hence promotes code reuse. The subclass itself can add its own new behaviour and properties. The java.lang.Object class is always at the top of any Class inheritance hierarchy.

Look at this vehicle class:

public class Vehicle
{
        protected String licensePlate;
        protected double speed=0.0;
        protected double maxSpeed;
        protected String make;
        protected String model;
        protected int year;
        protected int numberPassengers;
        protected int numberWheels=4;

        public Vehicle(String iPlate, double iSpeed, double iMaxSpeed,
        String iMake, String iModel, int iYear, int iNumberPassengers)
        {
            licensePlate=iPlate;
            speed=iSpeed;
            maxSpeed=iMaxSpeed;
            make=iMake;
            model=iModel;
            year=iYear;
            numberPassengers=iNumberPassengers;
        }

        public String getLicensePlate()
        {
            return licensePlate;
        }

        public String getMake()
        {
            return make;
        }                
}//class

This car class extends (Inherits) all of the methods and attributes from the Vehicle class above!

public class Car extends Vehicle
{
    protected int numberDoors;

    public Car(String iPlate, double iSpeed, double iMaxSpeed, String iMake,
        String iModel, int iYear, int iNumberOfPassengers, int iNumberOfDoors)
    {
        super(iPlate, iSpeed, iMaxSpeed, iMake, iModel, iYear, iNumberOfPassengers);
        numberDoors = iNumberOfDoors;
    }//constructor

    public int getNumberOfDoors()
    {
        return numberDoors;
    }//getNumberOfDoors

}//class

When we create a new car in the main class below, we can call vehicle methods and access vehicle attributes from the car class:

class Example_16_1
{
    public static void main(String args[])
    {
        Car c = new Car("96 C 1", 0.0, 123.45, "Toyota", "Corolla", 1998, 4, 5);

        System.out.println(c.getLicensePlate() + " is a " + c.getMake() +
        " car with " + c.getNumberOfDoors() + " doors.");
    }//main

}//class

Encapsulation

Encapsulation (data hiding) is probably the easiest Object Orientated principle for a student to understand and use. When you design a class in Java, you encapsulate it's attributes by declaring them as private and then creating public getter and setter methods for them as needed! If a field (attribute) is declared private, it cannot be accessed by anyone outside the class, thereby hiding the fields within the class. For this reason, encapsulation is also referred to as data hiding. Encapsulation can be described as a protective barrier that prevents the code and data being randomly accessed by other code defined outside the class. Access to the data and code is tightly controlled by an interface (The getter and setter methods you create).

The main benefit of encapsulation is the ability to modify our implemented code without breaking the code of others who use our code. With this feature Encapsulation gives maintainability, flexibility and extensibility to our code.

See previous getter and setter article for example!

Polymorphism

Polymorphism gives us two features you should be aware of when writing Java, Method Overriding and Method Overloading. Both of these involve creating two methods of the same name and having the Java Compiler automatically decide which one to use.

Method Overriding

Lets go back to the Car and Vehicle example from above. If we created a method in the vehicle class (lets call it printStatement()) we know that instances of Car can reuse this method through inheritance. But lets say that we don't want Car's to reuse this method, instead we want Cars to print their own statement. In this case we would override the printStatement() method. Lets look at this in code:

public class Vehicle
{
        protected String licensePlate;
        protected double speed=0.0;
        protected double maxSpeed;
        protected String make;
        protected String model;
        protected int year;
        protected int numberPassengers;
        protected int numberWheels=4;

        public void printStatement(){
            System.out.print("I am a vehicle");
        }

        public Vehicle(String iPlate, double iSpeed, double iMaxSpeed,
        String iMake, String iModel, int iYear, int iNumberPassengers)
        {
            licensePlate=iPlate;
            speed=iSpeed;
            maxSpeed=iMaxSpeed;
            make=iMake;
            model=iModel;
            year=iYear;
            numberPassengers=iNumberPassengers;
        }

        public String getLicensePlate()
        {
            return licensePlate;
        }

        public String getMake()
        {
            return make;
        }                
}//class

This car class extends (Inherits) all of the methods and attributes from the Vehicle class above!

public class Car extends Vehicle
{
    protected int numberDoors;

    public void printStatement(){
            super.printStatement();
            System.out.print(" but I am also a Car");
    }

    public Car(String iPlate, double iSpeed, double iMaxSpeed, String iMake,
        String iModel, int iYear, int iNumberOfPassengers, int iNumberOfDoors)
    {
        super(iPlate, iSpeed, iMaxSpeed, iMake, iModel, iYear, iNumberOfPassengers);
        numberDoors = iNumberOfDoors;
    }//constructor

    public int getNumberOfDoors()
    {
        return numberDoors;
    }//getNumberOfDoors

}//class

When we create a new car in the main class below, we can call vehicle methods and access vehicle attributes from the car class:

class Example_16_1
{
    public static void main(String args[])
    {
        Car c = new Car("96 C 1", 0.0, 123.45, "Toyota", "Corolla", 1998, 4, 5);

        c.printStatement();
    }//main

}//class

In the above example, we added a printStatement() method in both the Vehicle and the Car class. Because Car inherits from Vehicle, the printStatement() method is overridden in Car. Thus, when we call printStatement() on a Vehicle it does one thing but when we call it on a Car it does its own thing. In this case we decided to use super.printStatement() inside the Car printStatement() method. This means the Car printStatement() method calls the Vehicle printStatement() before doing its own thing. Hence the output of the above program will be:

I am a vehicle but I am also a Car

Method Overloading

This also involves creating two methods of the same name but in this case, both methods are in the same class. As you know by know, methods can accept variables as parameters(within brackets). Overloading methods involves creating two methods with same name but they accept either a different number or different types of variables. This is probably easier to explain in code:

public class MethodOverloadingExample{

    //prints a message
    public void printMessage(String message){
        System.out.println(message);
    }

    //prints a message a given number of times
    public void printMessage(String message, int numberOfTimes){
        for(int i = 0; i < numberOfTimes; i++){
            System.out.println(message);
        }            
    }

}

When I call printMessage from the main class, the Java Compiler looks at what I have put in the brackets of the method calls to decide what to run. Lets look at the main class:

public class main{

    public static void main(String args[]){
        MethodOverloadingExample messagePrinter = new MethodOverloadingExample();

        messagePrinter.printMessage("Hello World"):        
        messagePrinter.printMessage("Hello World", 10):

    }
}

The first call will call the first method, printing Hello World just once. The second call will call the second method, printing Hello World 10 times.

Video Tutorial