Hi! We'll devote today's lesson to Encapsulation in Java and begin with examples right out of the gate:)
Here you have an ordinary soda dispensing machine. I've got one question for you: how does it work? Try to give a detailed answer: Where does the soda come from? How is the internal temperature maintained? Where is the ice stored? How does the machine know which syrup to add?
You probably don't have answers to these questions. Okay, maybe not everyone uses these machines. They aren't that popular at present.
Let's try another example. Something you definitely use many times everyday. Oh, I have an idea!
Tell me how the Google search engine works. How exactly does it search for information on the words you enter? Why are these results at the top and not others?
Even though you use Google everyday, you probably don't know. But that doesn't matter. After all, it's not something you need to know.
You can use a search engine without thinking about exactly how it works. You can buy soda from a machine without knowing how it's built. You can drive a car without delving into how the internal combustion engine works and without even knowing high-school physics.
This is all possible thanks to one of the main principles of object-oriented programming: encapsulation.
In reading different articles on the topic, you must have encountered two widespread programming concepts: encapsulation and information hiding. As it happens, different people understand the word 'encapsulation' to mean different things. We'll decipher both terms so you have a complete understanding.
In programming, the original meaning of encapsulation was combining data and methods for working with that data in one package ("capsule").
In Java, encapsulating package is the class. The class contains both data (fields) and methods for working with that data.
This may seem obvious to you, but everything is arranged differently in other programming paradigms. For example, in functional programming, data is strictly separated from data operations.
In object-oriented programming (OOP), programs consist of capsules (classes) consisting of both data and functions for working with data.
Now let's talk about information hiding
How do we use all sorts of complex mechanisms without understanding how they're built or how they work? It's simple: their creators provided simple and convenient interfaces. On a soda machine, the interface is the buttons on the front panel. One button lets you choose the cup size. You choose the syrup with a second button. A third is responsible for adding ice. And that's all you need to do. It doesn't matter what the machine looks like inside. The important thing is that it's designed so that the user gets soda by pushing three buttons. The same thing applies to a car. It doesn't matter what's going on inside. The important thing is that when you press the right pedal the car moves forward, and when you press the left pedal the car slows down. This is the essence of information hiding. All of a program's 'innards' are hidden from the user. Such information is superfluous or unnecessary for the user. The user needs an end result, not an internal process. For an example, let's take a look at the Vehicle class:
public class Vehicle {
public void gas() {
/* Some complicated things happen inside a car.
As a result, it moves forward */
}
public void brake() {
/* Some complicated things happen inside a car.
As a result, it slows down */
}
public static void main(String[] args) {
Vehicle vehicle = new Vehicle();
// How everything looks to the user
// Press one pedal, the car moves
vehicle.gas();
// Press the other pedal, the car brakes
vehicle.brake();
}
}
This is how the implementation is hidden in a Java program. Just like in real life: the user is provided with an interface (methods). In a program, if you need a car to perform an action, you simply call the desired method. What happens inside these methods is superfluous. What matters is that everything works as it should.
Here we've been talking about implementation hiding. Java also has data hiding. We wrote about it in the lesson about getters and setters, but a reminder won't hurt.
For example, we have a Cat class:
public class Cat {
public String name;
public int age;
public int weight;
public Cat(String name, int age, int weight) {
this.name = name;
this.age = age;
this.weight = weight;
}
public Cat() {
}
public void sayMeow() {
System.out.println("Meow!");
}
}
Maybe you remember from a past lesson what the problem is with this class? If not, let's recall.
The problem is that its data (fields) are open to everyone. Another programmer could easily create a nameless cat with a weight of 0 and age of -1000 years:
public static void main(String[] args) {
Cat cat = new Cat();
cat.name = "";
cat.age = -1000;
cat.weight = 0;
}
In this situation, you could carefully track whether one of your colleagues is creating objects with invalid state, but it would be much better to eliminate even the possibility of creating these invalid objects.
We achieve data hiding with the help of:
- access modifiers (private, protected, package default);
- getters and setters.
Encapsulation gives us several important advantages:
- Monitoring correct object state. We gave examples of this above: thanks to the setter and private modifier, we've secured our program against cats with a weight of 0.
- User-friendly interface. We leave only methods exposed to the user. The user simply needs to call them to get a result. And there's no need whatsoever to delve into the details of how they work.
- Code changes don't affect users. We make all changes inside of methods. This doesn't affect users: they wrote vehicle.gas() to apply the gas, and that's what they will keep on doing. The fact that we changed something inside the gas() method remains invisible: as before, they simply get the required result.
GO TO FULL VERSION