Encapsulation beyond Definition — Object Oriented Programming (OOP)

Aditya Solge
Nerd For Tech
Published in
7 min readMar 25, 2024

I often encounter developers who grasp Object-Oriented Programming (OOP) concepts but struggle to apply that knowledge in real-life scenarios or articulate concepts effectively during interviews. Today, I’m diving deep into Encapsulation, a fundamental concept of OOP. I’ll take a different approach by starting with a real-life problem, devising a solution, and then revisiting the official definition of encapsulation to see if it makes much more sense.

Let’s imagine you need to represent an AdultPerson in OOP. The instinctive step is to create a Class for this purpose. So, let’s craft an AdultPerson class, keeping it simple with just two properties: name and age. Below is a straightforward implementation in Java:

class AdultPerson {
String name;
int age;
}

class Main {
public static void main(String[] baburao) {
AdultPerson p1 = new AdultPerson();
p1.name = "Baburao";
p1.age = 60;
System.out.println("Name: " + p1.name);
System.out.println("Age: " + p1.age);
}
}

We created two classes: one is the AdultPerson class, and the other is the Main class. For the purpose of this blog, we’ll assume that Main is the user of the beautiful software you are creating. Let’s talk about the problems now.

Problem #1: Negative Age

What is stopping Main from assigning a negative value to age? Nothing, because age is an integer, and in Java, you can assign both negative and non-negative values to age. And since we’re not violating any Java rules, the program will work just fine.

class AdultPerson {
String name;
int age;
}

class Main {
public static void main(String[] baburao) {
AdultPerson p1 = new AdultPerson();
p1.name = "Baburao";
p1.age = -10;
System.out.println("Name: " + p1.name);
System.out.println("Age: " + p1.age);
}
}

When viewed from a human perspective and considering the definition of an AdultPerson that we’re all aware of, this approach doesn’t align well. It’s not feasible for an AdultPerson to have a negative age, and certainly not a very large integer value, such as 10,000. Considering that Main is not Java but most likely a human, we need to address this discrepancy.

One way to handle this is by creating a method that checks the value of the age and provides the correct prompt to the user.

class AdultPerson {
String name;
int age;

public void setAge(int newAge) {
if (newAge < 18 || newAge > 150) {
System.out.println("Age must be between 18-150");
} else {
age = newAge;
}
}
}

class Main {
public static void main(String[] baburao) {
AdultPerson p1 = new AdultPerson();
p1.name = "Baburao";
p1.age = -10;
System.out.println("Name: " + p1.name);
System.out.println("Age: " + p1.age);
}
}

The output of the above program is as follows:

Name: Baburao
Age: -10

But why? This leads us to our next problem.

Problem #2: Direct access to Age

The AdultPerson class, although it contains a setAge method which checks for valid values of the age, still provides direct access to its internal member ‘age.’ So, how do we fix this? We’ll use the private access modifier/specifier to ensure that only the AdultPerson class has access to the age member.

class AdultPerson {
String name;
private int age;

public void setAge(int newAge) {
if (newAge < 18 || newAge > 150) {
System.out.println("Age must be between 18-150");
} else {
age = newAge;
}
}
}

class Main {
public static void main(String[] baburao) {
AdultPerson p1 = new AdultPerson();
p1.name = "Baburao";
p1.age = -10;
System.out.println("Name: " + p1.name);
System.out.println("Age: " + p1.age);
}
}

And now, when we compile the code, it gives the following error message.

Example.java:18: error: age has private access in AdultPerson
p1.age = -10;
^
Example.java:20: error: age has private access in AdultPerson
System.out.println("Age: " + p1.age);
^
2 errors

Hence, the only choice Main has now is to use the setAge method if they want to assign any value to the age.

class AdultPerson {
String name;
private int age;

public void setAge(int newAge) {
if (newAge < 18 || newAge > 150) {
System.out.println("Age must be between 18-150");
} else {
age = newAge;
}
}
}

class Main {
public static void main(String[] baburao) {
AdultPerson p1 = new AdultPerson();
p1.name = "Baburao";
p1.setAge(-10);
System.out.println("Name: " + p1.name);
System.out.println("Age: " + p1.age);
}
}

All is well; Main can use the set method to assign a value. But wait, how does Main access the value of age?

Problem #3: Accessing value of private member

To allow Main to access the latest value of age, we must provide it with a public method that returns the value of age.

class AdultPerson {
String name;
private int age;

public void setAge(int newAge) {
if (newAge < 18 || newAge > 150) {
System.out.println("Age must be between 18-150");
} else {
age = newAge;
}
}

public int getAge() {
return age;
}
}

class Main {
public static void main(String[] baburao) {
AdultPerson p1 = new AdultPerson();
p1.name = "Baburao";
p1.setAge(-10);
System.out.println("Name: " + p1.name);
System.out.println("Age: " + p1.getAge());
}
}

Now, the output of the program is as follows:

Age must be between 0-150
Name: Baburao
Age: 0

But wait, the output is showing 0 as the age. Isn’t that incorrect? Baburao is actually older than 0 years.

Problem #4: Object creation without valid values

We know the age of an AdultPerson must be at least 18 years, but even after encountering an error, Main is still able to print the Age as 0. So how do we fix this? Instead of just an error message, we can throw an exception to signal that something is wrong.

class AdultPerson {
String name;
private int age;

public void setAge(int newAge) {
if (newAge < 18 || newAge > 150) {
throw new RuntimeException("Age must be between 18-150");
} else {
age = newAge;
}
}

public int getAge() {
return age;
}
}

class Main {
public static void main(String[] baburao) {
AdultPerson p1 = new AdultPerson();
p1.name = "Baburao";
p1.setAge(-10);
System.out.println("Name: " + p1.name);
System.out.println("Age: " + p1.getAge());
}
}

And now, the output of the program is as follows:

Exception in thread "main" java.lang.RuntimeException: Age must be between 18-150
at AdultPerson.setAge(Example.java:7)
at Main.main(Example.java:22)

Hence, Main is unable to see 0 as the age. But what if Main never calls the setAge method and directly calls the getAge method?

class AdultPerson {
String name;
private int age;

public void setAge(int newAge) {
if (newAge < 18 || newAge > 150) {
throw new RuntimeException("Age must be between 18-150");
} else {
age = newAge;
}
}

public int getAge() {
return age;
}
}

class Main {
public static void main(String[] baburao) {
AdultPerson p1 = new AdultPerson();
p1.name = "Baburao";
System.out.println("Name: " + p1.name);
System.out.println("Age: " + p1.getAge());
}
}

The output will display 0 as the age.

Name: Baburao
Age: 0

This leads to our last problem.

Problem #5: Access value without assigning it the value

So, how do we solve it? We can require the mandatory input in the constructor and prevent Main from creating an object if the age is not valid.

class AdultPerson {
String name;
private int age;

public AdultPerson(int newAge) {
setAge(newAge);
}

public void setAge(int newAge) {
if (newAge < 18 || newAge > 150) {
throw new RuntimeException("Age must be between 18-150");
} else {
age = newAge;
}
}

public int getAge() {
return age;
}
}

class Main {
public static void main(String[] baburao) {
AdultPerson p1 = new AdultPerson(-10);
p1.name = "Baburao";
System.out.println("Name: " + p1.name);
System.out.println("Age: " + p1.getAge());
}
}

This way, we have ensured that the value of age is always between 18 and 150. If anyone tries to assign any other value, they will not be able to do so!

Encapsulation

In examining the final version of the AdultPerson class, we can confidently state that any object of AdultPerson will always possess a valid value for age. Therefore, it is safe to assert that an AdultPerson object will consistently remain in a valid state. This encapsulation definition resonates with me: encapsulation ensures an object remains in its valid state.

In our pursuit of achieving a valid state for AdultPerson, we utilized the following tools:

  1. Private access modifier.
  2. Setter-Getter methods to provide controlled access to private properties.
  3. Constructor to enforce the assignment of a valid value to age.
  4. Exception handling to prevent users from creating an object if a valid value is not provided.

However, it’s important to note that the tools we employed do not necessitate the use of all of them to achieve encapsulation. For some classes, merely having setter-getter methods will suffice, while for others, creating a parameterized constructor may also be necessary. The choice depends entirely on the business logic or rules you are working with, determining which of the mentioned tools to employ in achieving encapsulation.

Let’s consider the official definitions:

  1. Data Encapsulation, also known as data hiding, is the mechanism whereby the implementation details of a class are kept hidden from the user. The user can only perform a restricted set of operations on the hidden members of the class by executing special functions commonly called methods to prevent attributes of objects from being easily viewed and accessed
  2. Encapsulation in OOPs is the concept of binding fields (object state) and methods (behavior) together as a single unit.

I hope the above definitions (along with many others found over the internet) will now make much deeper sense to you than before.

If you found this blog useful, please give it Clap(s) and consider following for more such blogs. If you’re interested in learning about another topic in detail, feel free to comment down below.

For those who prefer watching videos, check out my YouTube channel at https://www.youtube.com/@gogettergeeks. For additional information about me and my initiative, visit my website at https://www.gogettergeeks.com/.

--

--

Aditya Solge
Nerd For Tech

I'm a full-time freelance software developer who used to work at Amazon. I help students with affordable learning at www.gogettergeeks.com.