On the quest to understand Object Oriented Programming, a traveler must pass through a cathedral supported by The Four Pillars of OOP. In an effort to solidify my understanding of these pillars, I’ve summarized the concepts in a handy blog post!
When discussing Object Oriented Programming, it’s common to use examples of classes that represent physical objects: a Dog class that can bark() or a Car class that has a make and model. Though these are great ways to teach the concepts, I wanted to challenge myself to use examples of built-in Java objects to learn a bit more about how these pillars are practically used.
Encapsulation is the concept of associating attributes and methods to an object through a class. When defining a class, we can set certain attributes that any instance of this class will possess. When the makers of Java created Vectors, for instance, they assigned them the attributes capacityIncrement, elementCount, and elementData. When we use a Vector, we can assign it a capacityIncrement and know that, when capacity is reached, the Vector’s capacity will grow by that amount. If we wanted to know the current capacity of our Vector, we could use capacity(), a method that has been encapsulated in the class. Encapsulation represents the idea that these attributes and methods are “inside” of our classes — they are a part of them.
Abstraction is the concept of a “black box,” or processes being executed “under the hood.” In Object Oriented Programming, this means that our objects should only reveal operations relevant to other classes. When we call toUpperCase() in Java, a lot of internal logic is executed. We don’t have access to every variable inside of that method because they’re not important to us in that moment. We don’t need to know the ascii value of each letter in our string — we just want to change our characters from lowercase to uppercase. Abstraction allows for this while cutting out the noise.
Inheritance means passing traits from one class to another, specifically from a parent class (known as a superclass) to its child. Rather than re-define attributes or methods in every single class we create, we can use inheritance to share the commonalities and then add new stuff that makes the class unique. In Java, Integer and Double are different classes — they have unique behaviors and attributes. But they can both use == (comparative equals operator) in the same way. The equals comparison is inherited along with other common operations from their shared ancestor, Number.
I view Polymorphism as a compliment to Inheritance. The value of Inheritance is that sibling classes can have common features that save us from writing duplicate code, but these classes must by definition have some uniqueness to justify their existence. Polymorphism is the ability to change or distinguish the methods or attributes of a class. Consider our old friend Vector in relation to ArrayList: these two share a lot in common — they’re children of the List interface because they store pieces of data in a specific order. Vector is unique in its capacity limit — ArrayLists don’t keep track of capacity in the same way and therefore do not have a capacity() method. Polymorphism can also mean that the same method call will take different actions depending on the class. toString() can be called on primitive data types including int and boolean, but it certainly doesn’t run the same code when converting an integer to a string compared to a boolean value.
Many of these blurbs are summarizations of complex concepts that I put into my own words, so I encourage any readers who want to learn more to check out my sources. Good luck on your journey!