Object-Oriented Programming
Object-Oriented Programming is a programming paradigm based on concepts of “objects”. Objects can refer to many real-world objects such as a car, a phone, a TV, etc, anything that possesses a state and some behaviours. Your TV has an “on” state, an “off” state and some behaviours such as “turn-on”, “turn-off”, and “change channel”. This programming model has been developed to mimic real-world scenarios in code and develop software accordingly. Object-Oriented Programming is about bundling your data and methods into classes so every instance(object) of that class can access it, as opposed to Procedural Programming which is about writing methods that perform operations on data.
I believe OOP is understood best through examples and some code and so that’s what we will be doing today. I will be using Java to demonstrate these concepts as I feel it is one of the best languages for learning OOP.
Classes and Objects
A class is a code block that contains all your data and methods which can then be used by the object of that class. A class can be referred to as a blueprint through which various Objects get created. For example, imagine yourself as an alien who had visited earth recently and is now tasked to replicate the human species to one day infiltrate planet earth with these humanoid species and take over the whole planet. To complete this task, you might as well start developing a blueprint:
class Human {
String gender;
float height;
float weight;
int age;
void setWeight(float newWeight) {
weight = newWeight;
}
void setHeight(float newHeight) {
height = newHeight;
}
void hasBrain() {
System.out.print("Unable to locate");
}
void increaseAge() {
age += 1;
}
}
By using this blueprint, you can create as many humans as you like with different properties. We use the objects to access the attributes(data) and methods of the class. The attributes can be perceived as the state of your object such as the height and the weight of a human in this case, while the methods are the behaviours of your object such as setWeight and setHeight which can be used to interact with the outside world. Here’s how we can create two Objects of our Human class and invoke its methods.
Human john = new Human();
Human jim = new Human();
john.setWeight(70);
john.setHeight(6.0);
john.increaseAge();
jim.setWeight(90);
jim.setHeight(5.8);
jim.increaseAge();
Constructor and ‘this’ keyword
A constructor is a function that gets called the moment we instantiate a new object of a class. A constructor always bears the same name as that of the class. Let’s have a closer look:
class Human {
String gender;
float height;
float weight;
int age;
human(String newGender, float newWeight, float newHeight, int newAge) {
this.gender = newGender;
this.weight = newWeight;
this.height = newHeight;
this.age = newAge;
}
void setWeight(float newWeight) {
this.weight = newWeight;
}
void setHeight(float newHeight) {
this.height = newHeight;
}
void hasBrain() {
System.out.print("Unable to locate");
}
void increaseAge() {
this.age += 1;
}
}
Human john = new Human('Male', 70, 6.0, 25);
System.out.print(john.gender) =>>> "Male"
While instantiating the “john” object we are passing four arguments: the gender, the weight, the height and the age of this particular human model. At this moment, the compiler calls the constructor function and sets the values of our fields/attributes using the ‘this’ keyword. It is a special keyword used to set the fields of that instance of the class. So, when we create an object “john”, ‘this’ is referring to the data of the john object. If we create a “jim” object then it would refer to or set the data for the “jim object”. Within a method or a constructor, ‘this’ is a reference to the current object — the object whose method or constructor is being called.
The four principles of Object-Oriented Programming
Now that we are aware of the basics of OOP, let’s look at the four core concepts.
Data Encapsulation
Encapsulation is a process of bundling code and data together into a single unit and restricting direct access to the data members of the class.
In simpler terms: You make your attributes private to eliminate their access from outside.
Let’s understand through the human class we created:
class Human {
private String gender;
private float height;
private float weight;
private int age;
private long modelNumber;
public human(String newGender, float newWeight, float newHeight, int newAge) {
this.gender = newGender;
this.weight = newWeight;
this.height = newHeight;
this.age = newAge;
}
public void setWeight(float newWeight) {
this.weight = newWeight;
}
}
In the above example, I have used two keywords of the Java language: Private and Public. These are access modifiers and they determine whether the data or method is accessible from outside the class. If your data or method is declared as private then they can only be accessed inside the class. So, you cannot do john.weight = 60 anymore because it will throw a compile-time error as the weight variable is private. However, the weight variable can be accessed from inside the class by any instance method such as the setWeight method in this case. If the attribute or method is declared public then it can be accessed from anywhere.
There are a couple of reasons why the data members(attributes/fields) should be kept private. In our example, the modelNumber field will be unique for every human we create. So, not everyone must be able to access this number and maybe accidentally alter it. This is the essence of encapsulation: hiding away private details to shield them from the outside.
Generally, your data members are private and your data methods(instance methods) are public so they can be used to alter your data members and thus cutting off direct access to them. This is an example of a well-encapsulated class.
Abstraction
Abstraction is the process of hiding away the intricate inner workings of a class and instead, providing a clean and easy-to-use interface via the class member functions.
Say we had a method called “walk()” in our Human class and any alien could use this method to make their human walk. Now, we don’t want the alien to be able to understand the gory inner workings of a method for him to use it, instead, we would much prefer him to just use this method to complete his task.
One example of abstraction is the methods we access after creating an array in many class-based languages such as Java, Python or Javascript. Say we want to sort an array. There’s a “sort()” or similar method in all three of the languages mentioned and we can sort our array using it even though we may not know how the algorithm is sorting the elements.
In Java, we can create abstract classes which are classes that can’t be instantiated and instead have to be subclassed. The methods in an abstract class don’t have a body and when an abstract class is subclassed, the subclass usually provides implementations for all of the abstract methods in its parent class.
Abstraction can be seen in every gadget or every software you use. From creating a post on a social media platform to turning up the volume of your TV, you don’t know exactly how everything is functioning but you don’t need to.
Inheritance
Inheritance is a mechanism in which one class acquires all the properties and methods of another class. A class that’s acquiring is called a subclass or child class and the class from which everything is acquired is called a superclass or parent class. Inheritance is pretty beneficial because we can inherit all the functionalities from the parent class and so we don’t need to rewrite them again in the child class. A child class can only have one parent class as Java doesn’t support multiple inheritance, although it is possible through interface. A parent class can have multiple child classes though. Consider a Dog class that extends an Animal class example :
class Animal {
public int age;
public animal(int newAge) {
this.age = newAge;
}
public void bark() {
System.out.print("I can talk!!");
}
public void identifyMyseld() {
System.out.print("I am an Animal");
}
class Dog extends Animal {
public String colour;
public dog(int newAge, String newColor) {
super(newAge)
this.color = newColor;
}
public void bark() {
System.out.print("wooooofff!!");
}
public void identifyMyseld() {
System.out.print("I am a Dog");
}
}
Dog dog = new Dog(4, "golden")
dog.identifyMyself()
//Output: "I am a Dog"
For the child class to inherit the parent class, we use the ‘extends’ keyword in Java. From the above example, the Dog class inherits all the properties and methods and can either use them as they are or can override them as we have done here in the bark and identifyMyself methods. The subclass constructor always calls the superclass constructor by using the super keyword in Java. If the superclass contains an argument constructor then a subclass must call the constructor or else the compiler will throw an error. All the animals that share the same characteristics as the Animal class can inherit it and use its methods and attributes.
Polymorphism
Polymorphism is a concept in which an object can take many forms. With regards to OOP, there are two types of Polymorphism: Compile Time Polymorphism and Run-Time Polymorphism.
As the name suggests, Compile Time Polymorphism is implemented at compile time. An example of it is method overloading. Method overloading occurs when we have several methods of the same name. Suppose we have two methods of the name ‘walk’, each with a different method signature(method signature comprises the name, number and type of parameters, and the return type). The compiler will be able to differentiate between these methods when they are called based on their method signature. This is known as method overloading.
Run-Time Polymorphism occurs, as you might have guessed already, at runtime. We have already seen what method overriding looks like in our example of inheritance when we called the ‘bark’ and the ‘identifyMyself’ methods. The compiler will override the method in the superclass and run the contents of the method present in the subclass. This is known as method overriding.