Since the field of software development is always evolving, it is important for programmers to update their knowledge in order to stay competitive. Learning Object-oriented programming (OOP) can help you stay relevant in the industry. OOP is fundamentally centered on the idea of objects, which are units of unified action and data.
OOP is defined by four key principles: encapsulation, data abstraction, polymorphism, and inheritance. The four pillars of software programming are another name for these. Now let’s go through each of them in this blog.
1. Encapsulation
The first pillar that we are going to talk about is Encapsulation. It is the process of containing all pertinent data inside an object and only revealing a tiny amount of it to the outside. The properties and behaviors are determined by the code within the class template. Encapsulation shields both the internal code implementation within a class and the internal data of objects.
Here is code example that implements encapsulation:
class Animal{
private String name;
private String species;
private int age;
public Animal(String name, String species, int age){
this.name = name;
this.species = species;
this.age = age;
}
public String getName() {
return name;
}
public String getSpecies() {
return species;
}
public int getAge() {
return age;
}
}
In this instance, the Animal class encapsulates its properties (name, species, age) by marking them as private and offering public getter methods for retrieval.
2. Inheritance
Child classes derive both data and behaviors from the parent class, extending its functionality by adding supplementary attributes and behaviors. The parent class can establish fundamental attributes and behaviors, thereby enabling the creation of child classes.
class Animal {
private String name;
private String species;
private int age;
public Animal(String name, String species, int age) {
this.name = name;
this.species = species;
this.age = age;
}
public String getName() {
return name;
}
public String getSpecies() {
return species;
}
public int getAge() {
return age;
}
}
class Lion extends Animal {
private boolean isKing;
public Lion(String name, int age, boolean isKing) {
super(name, "Lion", age);
this.isKing = isKing;
}
public boolean isKing() {
return isKing;
}
}
In this example, the Lion class serves as a subclass inheriting traits from the Animal class. It introduces a new attribute, isKing, indicating whether the lion holds the status of king within the jungle.
3. Abstraction
Abstraction serves as a continuation of encapsulation, concealing internal program details from users through the utilization of classes and objects, which contain both data and code. Moreover, it plays a role in safeguarding confidential information stored within the source code.
abstract class Animal {
private String name;
private String species;
private int age;
public Animal(String name, String species, int age) {
this.name = name;
this.species = species;
this.age = age;
}
public abstract void makeSound();
public String getName() {
return name;
}
public String getSpecies() {
return species;
}
public int getAge() {
return age;
}
}
In this example, the Animal class is depicted as an abstract entity, outlining shared characteristics and featuring an abstract method makeSound(). This requirement obliges its subclasses to furnish a concrete implementation.
4. Polymorphism
Lastly, Polymorphism. It comes in two forms which are static and dynamic. Static polymorphism can be achieved through method overloading, and dynamic polymorphism is achieved through method overriding. In the former, a child class can present a distinct implementation compared to its parent class. In the latter, methods may share identical names, but the number of parameters passed into the method call may vary.
Dynamic Polymorphism
class Animal {
public void makeSound() {
System.out.println("Some generic sound");
}
}
class Lion extends Animal {
// Method overriding
@Override
public void makeSound() {
System.out.println("Roar");
}
}
public class Main {
public static void main(String[] args) {
Animal lion = new Lion();
lion.makeSound(); // Output: Roar
}
}
In this example, the makeSound()
method in the Lion
class overrides the method in the parent Animal
class. When we create an instance of Lion
and call makeSound()
, the overridden method in Lion
is executed, which demonstrates dynamic polymorphism.
Static Polymorphism
class Animal {
// Method overloading
public void makeSound() {
System.out.println("Some generic sound");
}
public void makeSound(String sound) {
System.out.println(sound);
}
}
public class Main {
public static void main(String[] args) {
Animal animal = new Animal();
animal.makeSound(); // Output: Some generic sound
animal.makeSound("Meow"); // Output: Meow
}
}
In this example, we overload the makeSound() function with two variations: one devoid of parameters and the other accepting a String parameter. We invoke the method with different arguments, and the compiler determines the appropriate version based on the method signature during compile time.
In a nutshell, Object-Oriented Programming (OOP) is essential for its ability to promote code organization, reusability, and maintainability. By encapsulating data within objects and abstracting away unnecessary complexities, OOP enhances code readability and scalability. Furthermore, OOP’s support for inheritance and polymorphism enables developers to efficiently extend and adapt their codebase to meet evolving requirements.