Let’s get introduced to the main principles of the object-oriented programming - one of the main programming methodologies, which is based on the idea that a program is a cluster of objects, each belonging to a certain class and the classes build up an inheritance hierarchy.
When developing an application component, one of the main principles consists in:
- Hiding of internal data and implementation details of the component from other application components.
- Providing a set of methods to interact with the component (API).
This principle is one of four fundamental principles of OOP and it is called encapsulation.
Correct encapsulation is important for many reasons:
1. It allows to reuse components. The components can be used in a wider context since they interact with each other only via API and are not sensitive to changes of the inner structure.
2. Encapsulation boosts the development process. The components loosely connected with each other (it means the components, whose code calls other components or uses their code as rarely as possible) can be developed, tested and supplemented independently.
3. Correctly encapsulated components are easier to understand and debug, which makes it easier to maintain an application.
In the Java language encapsulation is implemented via:
- System of classes which help accumulate information about an object in one place.
- Packages which group the classes by some criterion
- Access modifiers which can be used to mark the whole class or its field/ method.
There are four access modifiers:
- public – full access to the entity (field/ method of a class) from any package.
- protected – only the classes within the package and child classes have access to the entity.
- implicit default modifier (if three explicit modifiers are absent) – only the classes within the package have access to the entity.
- private – access only within the class in which the entity is declared.
For correct encapsulation, we also need a correct API to work with a component. For example, we can include the logic for the check of transmitted values to the variable setter or even omit providing setters in the class, if the class must be available only for reading.
An example of correct class encapsulation:
In the example above, a value for the name variable is set when an object is created, and this value cannot be changed from outside because there is no setter for the variable. In the setter for the age variable, the following is implemented:
- A check of whether a transmitted parameter is correct.
- An exception throw if a value is incorrect.
Inheritance is one of the main principles of the object-oriented programming since it allows to build up a hierarchical structure of an object. Using inheritance, you can create a general class, which will define characteristics and behavior for some cluster of linked objects. Later, this class can be inherited by other more private classes, each adding its unique characteristics and thus supplementing and changing behavior of the basic class. In the Java terminology, such general class is called superclass, or base class, or parent class and a class which inherits it is called subclass, or child class, or derived class.
Inheritance provides the “is-a” relationship between a superclass and a subclass. For example, there is the “Employee” class and the “Manager” class. Every manager is also a company employee, therefore there is the “is-a” relationship between the “Manager” class and the “Employee” class. Thus, from the inheritance point of view, the “Employee” is a superclass and the “Manager” - a subclass. Moreover, a class which inherits some other class can be a superclass itself for one or more other classes. Also, in comparison with, for example, C++, in Java, there is no, strictly speaking, the multiple inheritance, which means that any class can have no more than one superclass. And all classes for which a superclass is not clearly specified inherit the “Object” class by default.
The “Employee” class from the example above is a superclass not because it is superior to the “Manager” class or has more functionality. The opposite is true: the subclasses have no less but often much more functionality than their superclasses. The “super-” and “sub-” prefixes are borrowed from mathematics: the array of all employees contains the array of all managers. Thus, the array of all managers is a sub-array of the array of employees.
To inherit some class in Java the keyword extends is used:
In the example above, “Employee” is the superclass for the “Manager” class and “Manager” is the subclass of the “Employee” class. The “Employee” class abstracts basic characteristics of all company employees: first name, second name, salary, hire date, and the “Manager” class supplements these characteristics with the percent of bonus for managers and changes behavior of the getSalary() method via polymorphism.
Talking about polymorphism we should remember that this principle is inextricably connected with another principle of OOP – inheritance, which helps implement polymorphism.
Let’s take as an example an abstract class “Automobile”, which inherits two specific classes: “Sports car” and “Truck”.
Both sports cars and trucks have common characteristics and can perform the same actions as other automobiles. These actions are specified in the abstract superclass, but they can be performed in different ways.
For example, the “start the engine” action is common for all automobiles, but you can perform it by pressing the button in case with the sports car and by turning the ignition key in the case with the truck. Different solutions – one result. That’s what polymorphism is about.
To be more specific, polymorphism is one of the OOP principles, which allows to call an overridden method via a variable of a parent class and thus provoke a behavior which would correspond to the real subclass, to which this variable refers.
The code above is an example of polymorphism. At first, the object of the subclass “SportCar” is assigned to the variable of the superclass “Vehicle”. When calling the “start()” method, the console displays: "Starting my fancy sport car!"
If we assign the object of the subclass “Truck” to the same variable and call the same “start()” method, the console displays: "Starting my heavy truck!"
From relatively recent times, abstraction is singled out as an independent fourth principle.
One of the main definitions of the word “abstraction” which you can encounter in contemporary dictionaries:
Abstraction - the process of removing physical, spatial, or temporal details or attributes in the study of objects or systems to focus attention on details of greater importance.
All programming languages provide certain abstractions. For example, the languages of the assembler family are some kind of abstraction of corresponding microprocessors, since they allow us to distract our attention from details of their implementation and to interact with them via a certain set of instructions of a higher level. The imperative programming languages which followed assembler, for example Basic, Fortan, C were abstraction of a higher level in comparison with the assembler languages - they allowed to use some syntax more familiar to a human-being by making the syntax closer to natural languages.
The object-oriented languages like Java provide even higher level of abstraction: in OOP, objects are models of the real-world concepts like “employee”, “server”, “diary record”. The focus is put only to those properties of these concepts which are necessary in some specific case to solve some specific problem.
For example, in the application for tracking university students, the “Student” class, apart from general fields like first name, last name, date of birth and so forth, contains the fields with information about a gradebook number, a student status (active, on academic leave, expelled), faculty, grades for semesters and so forth. But such information is irrelevant for the same “Student” class in the application for tracking students of the EPAM Training Center. In this case, the class contains the fields standing for educational projects to which students are assigned, their English level according to the latest test results, the number of attended events and so forth.
That’s what abstraction is about: some object should fulfil some tasks, and a developer is focused on the object properties relevant for these tasks. As a result, in the object-oriented languages, developers are focused on the terms of the domain for which they develop the application, in comparison with the imperative languages, where developers should focus of the computer logic terms.