Object Oriented Programming paradigms polymorphism, inheritance, encapsulation, abstraction in java blog post lionguest studios website'

4 pillars of OOP: Object Oriented Programming Paradigms in Java Part 2/2

This is Part 2 of the object oriented programming series where we will be discussing the four object oriented programming paradigms in Java (4 pillars of oop). In our first part, we have discussed the basics of OOPs concepts like classes, objects, constructors, and many more. If you haven’t read that post then it’s recommended to do so as it will help you in understanding the concepts which we will be discussed in this blog post. The link to that first blog post can be found here 👉 (Object Oriented Programming (OOPs) concepts in Java Part 1/2).

This blog post will not be a structured kind of blog. We will explore various concepts of object oriented programming paradigm as we move forward while following a proper flow of information. Explaining these concepts individually will not make much sense since each of these concepts are interrelated to each other.

Before diving deep into the 4 pillars of oop lets first understand want are access modifiers in Java.

Access Modifiers in Java

The Access modifiers are reserved keywords in Java that can be used with variables, methods, constructors, and classes. They are used to restrict the scope of the variables, methods, constructors, and classes within classes and packages (In general, packages are also known as directory or a folder). The Access modifiers will help us in writing quality code as well as in enhancing the security of the software. There are four access modifiers in Java each of them are explained briefly.

access modifier
Image credits: studytonight.com

1. public – Whenever you use a public keyword before any variable or a method then that variable or a method can be accessed inside any class or packages.

2. default – By default, if you don’t use any access modifier before variables, methods, constructors, or classes then in that case java will consider them as default access modifiers. The default access modifier can be accessed only inside a particular package in which it’s declared as well as inside the sub-packages if present.

3. private – If the variables, methods or constructors are declared as private then they can be accessed only inside that class where they are declared. When using a private keyword with classes, only make inner classes or nested classes as private and not the outer class. If we declare the inner class as private then it can be accessed from the outer class, but if the outer class itself is made private then this makes no sense since you will not be able to access any elements present inside that class.

4. protected – If the variables, methods, or constructors are made protected then they can only be accessed within the package they are defined as well as inside the subclasses (with the help of inheritance). The classes cannot be made protected.

The 4 Pillars of OOP (object oriented programming) Paradigms in Java

Polymorphism, Inheritance, Encapsulation, and Abstraction are the 4 pillars of OOP (object oriented programming). Let’s understand each of them with an example. Imagine that we have a car manufacturing company and we want to design two cars, one is an SUV car and another one is a sports car. As we know, every car has various things in common and they have some differences as well. So, we will try to represent all of these things in object oriented way such that we can get a proper understanding of each of the 4 paradigms of the OOP.

First, we will create 4 classes, Main class, SUV class, Sports class, and Car class. The Car class will contain all the common properties of both the SUV and Sports cars. So, the Car class will be the parent of both SUV and Sports classes. The SUV and Sports classes will contain car-specific properties for SUV cars and Sports cars respectively. Inside the Main class, we will create the objects of these two car types.

Initially, this is how each of these classes will look like. You can create separate files for each class or you can also specify all these classes in a single file as well. The recommended way is to create separate files for each class.

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

    }
}
public class SUV extends Car{

}
public class Sports extends Car{

}
public class Car{

}

1. Polymorphism in Java

Whenever people buy a car they are more interested in knowing the speed of the car. So, as a car manufacture company we have to make an engine for both our SUV as well as for our sports car that could take our car’s speed to the next level. To add an engine to our car, we will create a method called “engine()” which will take a single parameter of type string called “engineType”. The value of the “engineType” variable will be “SUV Engine” or “Sports Engine”. You can pass in any values, this is just for the sake of this example.

The engine() method will go under our Car class because it doesn’t matter what type of car your building it will always have an engine. And depending upon the type of the car which we are building we will tell the engine() method to create an engine for that type of car. For example, if we are building SUV car then we will tell the engine() to create an engine for SUV car by passing the value “SUV Engine” to the “engineType” parameter inside the method.

We can also make this method “public” so that we can access it inside the “Main.java” as that’s where we will be creating all of our objects.

public class Car{

    public void engine(String engineType){
        System.out.println("Create Engine: " + engineType);
    }
}

In the case of Sports car, people are also interested in knowing how many seconds the car’s engine will take to go from 0 to 100 kmph. So, for this, we can also pass another parameter called “secondsToReachHundred” to the engine() method so that we can know in how many seconds our engine can reach 100 kmph. But if we pass this parameter to the above engine() method then it can cause a problem. Since whenever we will create the engine for our SUV class by calling the engine() method then during that time also we have to pass the value to the second parameter “secondsToReachHundred” as well since the engine() takes two parameters as input which we don’t want, Right.

To solve this, we can create another method specifically for our Sports car, so that whenever we will create the engine then we can call that method as shown below.

public class Car{

    // For the SUV and other car types
    public void engine(String engineType){
        System.out.println("Create Engine: " + engineType);
    }

    // For the sports car
    public void engine(String engineType, int secondsToReachHundred){
        System.out.println("Create Engine: " + engineType + secondsToReachHundred);
    }
}

As you can see above, we have created another method called engine() which takes two parameters “engineType” and “secondsToReachHundred”. This method can be called by any car that wants to know much time it will take for the engine to reach a hundred, but more specifically by the Sports car.

This is called polymorphism which is the first pillar out of the 4 pillars of OOP. In polymorphism, we can have various methods with the same name (in this case, engine) but each method will take different signatures or parameters. You can also create a method with the name “engine()” which can take no parameters as well. In this case, we don’t need a method with no parameters and that’s the reason we have not created such a method.

This is also an example of method overloading, as we are creating multiple methods with the same name but different parameters.

A Polymorphism is of two types:

a. Compile time polymorphism

In this type of polymorphism, Java can decide during the compile-time which of these methods needs to be called since all the methods have the same name. The example which we have seen above is the compile-time polymorphism as Java can decide which engine() method needs to be called based on the number of parameters passed to the method while calling the method. This is also known as static polymorphism.

b. Runtime polymorphism

Whenever we have a method present inside the parent class and we override that method in our child class then this is called as runtime polymorphism or dynamic polymorphism.

Overriding a method simply means that you already have a method defined in the parent class, but for some reason you don’t want to use that method, instead want to create your own so that you can define your own code into that method. In such cases, the method name and its parameters should remain same as that of the method present inside parent class while defining your own method in child class.

In our above example, we have engine(String engineType) method in out Car class which only prints a message to the output window. Now let’s override this method into the Sports class which is a child of the Car class. While doing so we will also create a boolean variable called “isElectric” whose value will be currently set to false which represents that the car engine doesn’t work on electric, as shown below.

public class Sports extends Car{

    boolean isElectric = false;

    @Override
    public void engine(String engineType){
        if(!isElectric){
             System.out.printf("The %s does not work on electric, it works on petrol. \n", engineType);
        }   
    }
}
public class Main {
    public static void main(String[] args) {
        Sports lamborghini = new Sports();
        lamborghini.engine("Sports Engine");
    }
}

In this case, while compilation the Java compiler will not know which engine() method you are exactly calling, either of the Sports class or of the Car class, since both of these classes have these methods present with the same name as well as with the same parameters or signature. This decision can only be taken during runtime only and therefore this is called runtime polymorphism or dynamic polymorphism.

If you run the above program at this stage then the output will be as follows.

The Sports Engine does not work on electric, it works on petrol.

2. Inheritance in Java

You might have not guessed it, but we have already seen inheritance above. The extend keyword used above represents the parent and child relationship. The class before the extend keyword is a child and the class after the extend keyword is a parent.

In our example, the SUV class and the Sports class both are children of the Car class since both can inherit and access the properties (variables and methods) present inside the Car class into their class. The Car class is the parent. This is called inheritance which is the second pillar out of the 4 pillars of OOP.

3. Encapsulation in Java

As we know, the variables which are made private cannot be accessed outside the class. This is good for security purposes because we don’t want all the classes to access these variables. But what if some of the classes want to access these variables?

To achieve this, we can use something called getter and setter methods. With the help of these methods, we can indirectly access the private variables of the classes. This helps us in achieving Encapsulation as we are enclosing the private variable with getter and setter methods because of which we cannot directly manipulate the private variable.

Let’s now create two private variables inside our SUV class. The first variable is the “navigationSystemSupported” which will tell whether the car supports a geographic navigation system or not and the second variable will “noOfSeats” which as the name suggests will tell the total number of seats the SUV car will have.

To create getter and setter methods for these two variables, on your IDE (I’m using Intellij IDEA) right-click and select “Generate”.

Click on generate in your IDE - lionguest studios 4 pillars for oop
Click on generate in your IDE

And after that choose “Getter and Setter”.

Choose getter and setter option from dropdown - 4 pillars of oop lionguest studios blog
Choose Getter and Setter option from dropdown

This will generate the below boiler plate code for you.

public class SUV extends Car{

    private boolean navigationSystemSupported;
    private int noOfSeats;

    public boolean isNavigationSystemSupported() {
        return navigationSystemSupported;
    }

    public void setNavigationSystemSupported(boolean navigationSystemSupported) {
        this.navigationSystemSupported = navigationSystemSupported;
    }

    public int getNoOfSeats() {
        return noOfSeats;
    }

    public void setNoOfSeats(int noOfSeats) {
        this.noOfSeats = noOfSeats;
    }
}

Now let’s access these variables from our Main class. To do this, we will first create the object of the SUV class using the new keyword. Then using the setter methods “setNavigationSystemSupported” and “setNoOfSeats” we will set the values of the variables navigationSystemSupported and noOfSeats as shown below.

SUV rangeRover = new SUV();
rangeRover.setNavigationSystemSupported(true);
rangeRover.setNoOfSeats(9);

As you can see above, we are not directly accessing the private variables present in the “SUV” class. With the help of methods, we are setting the values of the variables. This helps to prevent unauthorized access to the variable which eventually enhances security.

public class Main {

    public static void main(String[] args) {

        Sports lamborghini = new Sports();
        lamborghini.engine("Sports Engine");

        SUV rangeRover = new SUV();
        rangeRover.setNavigationSystemSupported(true);
        rangeRover.setNoOfSeats(9);

        if(rangeRover.isNavigationSystemSupported())
            System.out.printf("The SUV has %d seats and supports navigation system. \n", rangeRover.getNoOfSeats());
        else
            System.out.printf("The SUV has %d seats and does not supports navigation system. \n", rangeRover.getNoOfSeats());
    }
}

In the end, we are printing the values of the private variables using getter methods “isNavigationSystemSupported” and “getNoOfSeats” as shown above.

The Sports Engine does not work on electric, it works on petrol. 
The SUV has 9 seats and supports navigation system.

This is all about encapsulation which is the third pillar out of the 4 pillars of OOP.

4. Abstraction in Java

Abstraction is the last pillar out of the 4 pillars of OOP in Java. There is usually confusion between people when it comes to encapsulation and abstraction. Encapsulation, as we have already seen above is used to hide or encapsulate the data so that our data could remain secure.

In Abstraction, you might have seen this definition that “Abstraction is used to hide the implementation details”. But what exactly does it mean? The definition itself seems confusing 😂. So, let me clear this out of you.

In Abstraction, we usually make the class abstract and some or all of its methods abstract as well (we will see how to make a class or method abstract in few moments). This means that we don’t write the implementation or the body of the methods which are abstract. We only write the method name and specify the number of parameters it will take and then we put a semicolon as shown below.

# This is an abstract class
abstract class MyClassName{

    # This is an abstract method that doesn't have a body
    abstract int newMethod();
}

Now whichever class will inherit this abstract class, that class will always have to define or implement these methods in its class. So, here what’s happening is that the abstract method doesn’t have the implementation of the methods present in it. The implementation is present in some other classes which will inherit this abstract class. Therefore, the definition of abstraction states that it hides the implementation.

Abstraction can be achieved with the help of abstract keyword or by using interfaces

a. abstract keyword

  • A method or a class can be made abstract with the help of the “abstract” keyword.
  • An abstract class can contain both abstract methods as well as normal methods.
  • An Abstract method doesn’t have implementation.
  • An abstract class cannot be instantiated with the new keyword (i.e we can’t create objects of the abstract class).
  • To use the abstract class or its methods you can inherit this class using the “extend” keyword.

Let’s create a new abstract class named “CarFactory” where the cars are manufactured. Inside which we will be defining one normal method “totalCarsManufactured” which will tell us the total cars manufactured in the factory.

Also, we will define one abstract method called “carChassisAvailable” that will tell how many car chassis are available in the factory for both SUV and sports car. This abstract method will take two parameters “chassisSUV” (total chassis of SUV car available) and “chassisSports” (total chassis of sports car available) and the sum of both these values will be returned as an integer value.

// Abstract class
public abstract class CarFactory {

    // Normal method
    public void totalCarsManufactured(){
        System.out.println("A total of 50 cars are manufactured to date.");
    }

    // Abstract method with no implementation
    abstract int carChassisAvailable(int chassisSUV, int chassisSports);

}

We will write the implementation for the above abstract method inside the Car class as follows. Notice that we are extending the CarFactory class using extend keyword.

public class Car extends CarFactory{

    // For the SUV and other car types
    public void engine(String engineType){
        System.out.println("Create Engine: " + engineType);
    }

    // For the sports car
    public void engine(String engineType, int secondsToReachHundred){
        System.out.println("Create Engine: " + engineType + secondsToReachHundred);
    }

    // Implementation for the abstract method present in CarFactory
    @Override
    int carChassisAvailable(int chassisSUV, int chassisSports) {
        return chassisSUV + chassisSports;
    }
}

Now we can call the totalCarsManufactured() and carChassisAvailable() methods by creating an object of the Car class inside the Main class.

Car totalCars = new Car();
totalCars.totalCarsManufactured();
int chassis = totalCars.carChassisAvailable(40, 50);
System.out.println("Total Car Chassis Available: "+chassis);

The output of this will be look like this.

A total of 50 cars are manufactured to date.
Total Car Chassis Available: 90

b. Interfaces

Another way of achieving abstraction is by using interfaces.

  • To declare interface we use the “interface” keyword.
  • To use the interface we use the “implements” keyword.
  • An interface doesn’t allow normal methods as the abstract class does. It only allows abstract methods which provide total abstraction.
  • All methods present inside the interface have no implementation (methods without body).
  • No need to specify an abstract keyword before a method. By default, all the methods specified inside the interface are abstract.
  • Java doesn’t support multiple inheritance. But using interfaces we can achieve it.

Now here we will create a new file for our interface and will create an abstract method but this time using an interface.

The interface will also have a name, here we will give “CarFactoryInterface” as a name for our interface. Then we will define a “noOfAssemblyLines()” method that illustrates the number of assembly lines in the car factory and the implementation of this method will be written inside the Car class.

public interface CarFactoryInterface {

    void totalCarsManufactured();
    int carChassisAvailable(int chassisSUV, int chassisSports);
}

To use the interface in the Car class we have to use the implements keyword. Then we will define the noOfAssemblyLines() method inside the Car class.

public class Car extends CarFactory implements CarFactoryInterface{

    // For the SUV and other car types
    public void engine(String engineType){
        System.out.println("Create Engine: " + engineType);
    }

    // For the sports car
    public void engine(String engineType, int secondsToReachHundred){
        System.out.println("Create Engine: " + engineType + secondsToReachHundred);
    }

    // Implementation for the abstract method present in CarFactory
    @Override
    int carChassisAvailable(int chassisSUV, int chassisSports) {
        return chassisSUV + chassisSports;
    }

    // Implementation for the abstract method present in CarFactoryInterface
    @Override
    public void noOfAssemblyLines() {
        System.out.println("There are 2 assembly lines, one for SUV and another for Sports car.");
    }
}

If you noticed clearly then you will see we are accessing methods from two different classes CarFactory and CarFactoryInterface at the same time This is a kind of multiple inheritance. You can create any number of interfaces as you want and implement them using commas like “public class Example implements A, B, C”. But a class can only inherit from a single parent class using the extend keyword.

At the end, you can call the noOfAssemblyLines using the Car object inside the Main class.

package com.company;

public class Main {

    public static void main(String[] args) {

        Sports lamborghini = new Sports();
        lamborghini.engine("Sports Engine");

        SUV rangeRover = new SUV();
        rangeRover.setNavigationSystemSupported(true);
        rangeRover.setNoOfSeats(9);

        if(rangeRover.isNavigationSystemSupported())
            System.out.printf("The SUV has %d seats and supports navigation system. \n", rangeRover.getNoOfSeats());
        else
            System.out.printf("The SUV has %d seats and does not supports navigation system. \n", rangeRover.getNoOfSeats());


        Car totalCars = new Car();
        totalCars.totalCarsManufactured();
        int chassis = totalCars.carChassisAvailable(40, 50);
        System.out.println("Total Car Chassis Available: "+chassis);

        totalCars.noOfAssemblyLines();
    }
}

Finally, this is what you will see as an output.

The Sports Engine does not work on electric, it works on petrol. 
The SUV has 9 seats and supports navigation system. 
A total of 50 cars are manufactured to date.
Total Car Chassis Available: 90
There are 2 assembly lines, one for SUV and another for Sports car.

That’s all for this blog post 4 pillars of OOP (Object Oriented Programming) Paradigms in Java. Thanks for the read. If you like the content then support us on Patreon. Your support will surely help us in writing more of such content.

To read more such blogs about Object oriented programming related stuff visit our blogs page on LionGuest Studios.

1 thought on “4 pillars of OOP: Object Oriented Programming Paradigms in Java Part 2/2”

  1. Pingback: Object Oriented Programming (OOPs) Concepts In Java Part 1/2 | LionGuest Studios

Leave a Comment

Your email address will not be published. Required fields are marked *