We all know that OOPS, which stands for Object Oriented Programming System is one of the widely used concepts in programming. Its main aim is to hide complex details from the user while showing only the basic stuff like hiding how something works internally while showing only those parts that will be required to use it. This hiding of complex details in OOPS is known as abstraction. in this blog, we will mainly understand this concept in Java Programming Language thus Abstraction in Java.
As told above, we will be understanding this concept in Java Programming language thus we will be writing code in Java related to Abstraction. We will not only understand this concept with the help of codes, but also with the help of diagrams, and real-life examples to make it more interesting and intractable.
So, let’s start…
Index
- What is Abstraction in Java?
- Ways of Achieving Abstraction in Java.
- Abstract Class in Java
- Understanding Abstract Class with the help of a code
- Abstract Method
- Interface
- Understanding Interface with the help of code
- Nested Interface
- Difference between Abstract class and interface
- Similarity between Abstract Class and Interface
- When to use Abstract Class VS When to use interface
- Advantages of Abstraction in Java
- Disadvantages of Abstraction in Java
- Conclusion
- Abstraction in Java FAQ's
What is Abstraction in Java?
As told above, abstraction is one of the four pillars of OOPs which is used to hide complex details while displaying the ones which are easier to understand. Developers use it to hide all the functionality or the complex stuff which are irrelevant from the user’s point of view while showing only the essential features of an object to the outside world. Let’s understand this with the help of an example –
We all like watching TV while sitting on couches, sofas, etc, taking the remote and switching on the TV, changing channels to find our favourite shows, increasing or decreasing the volume or Binge watching and all these features are at our disposal, it is just a matter of clicking the buttons on the remote. But how.?
Well, the answer is rather simple, the internal structure of a remote is not as simple as its exterior (as shown in the above image), a series of things happen when we press a button. Let’s understand them one by one, but only briefly.
Signal Transmission: The remote sends an infrared signal to turn the TV on.
Signal Reception: The television has a receiver that catches this signal.
Processing the Signal: The TV processes this signal and responds by powering it up.
This entire process involves complex electronic principles and programming inside both the remote and the TV, yet all we need to know is which buttons to press for which action. So, with this principle in mind, we have abstracted or are unaware of all the complex things that occur inside both TV and remote as these details are not necessary for us to know.
Similarly, in programming when we use a class or a method in Java, we don’t need to understand the details of how the process is implemented. Just like using the remote, we only need to know what functions are available and how to call them. This simplifies our role as a programmer because we can use code written by others without needing to understand every detail about how it works—just what it does is enough.
Ways of Achieving Abstraction in Java.
There are two ways of achieving abstraction in Java, they are
- Abstract Classes
- Interfaces.
Let’s understand each of them
Abstract Class in Java
An abstract class is a class that is declared using the abstract keyword or has one or more abstract methods. This abstract keyword is the modifier and can be used with classes and methods, and not variables. It may or may not contain all the abstract methods (methods without a body) as some of them may be concrete methods (methods with a body)
abstract void moveTo(double deltaX, double deltaY);
public abstract class GraphicObject {
// declare fields
// declare nonabstract methods
abstract void draw();
}
Now the question comes that since the beginning as in from the time of DSA, we have only used the “new keyword” to create classes, objects and whatnot, so why are we using the abstract keyword here and not the new keyword?
Once again the answer is rather simple, that’s because –
- Abstract classes are incomplete as they contain abstract methods that do not have an implementation. Since the class is not fully defined, it cannot be instantiated.
- The size of an abstract class is unknown: When creating an object with the new keyword, the JVM needs to know the memory size to allocate. However since abstract classes can have abstract methods without an implementation, their size is still being determined.
- Calling abstract methods would lead to errors: If you could create an object of an abstract class and call its abstract methods, the JVM would need to know what to do since there is no implementation. This could lead to crashes.
- Abstract classes are meant to be extended: They are designed to serve as base classes that other concrete classes can extend and provide implementations for the abstract methods.
When a class extends an abstract class, it must either implement all the abstract methods of the parent class or be declared as an abstract class.
The purpose of an abstract class is to serve as the blueprint for other classes. It allows us to define a common structure and behavior that can be shared among multiple subclasses while enforcing certain rules and guidelines. Though it has a default constructor, it can also have a parameterised constructor.
When an abstract class is subclassed, it usually implements all of its parent class’s abstract methods. But for that to happen the subclass must also be declared abstract.
Key points to Remember
- Abstract class needs the abstract keywords to get initiated
- It can have both abstract and concrete methods
- They cannot be instantiated, but they can be subclassed.
- If a class contains an abstract method, it by default becomes an abstract class.
- It can have parameterised constructors, though the default constructors are always present.
- Abstract classes allow you to define common behaviour and properties that subclasses can inherit and specialise.
- They can extend only one class in either public, private or protected concrete methods.
- But can implement one or more interfaces
- We can declare fields that are neither static nor final
- A class can be both extended and implemented at the same time
<em>class MyClass extends AnotherClassName implements InterfaceName { }</em>
Understanding Abstract Class with the help of a code
Let’s understand abstract class with the help of a code.
abstract class Animal {
// Abstract method (no body)
abstract void makeSound();
// Concrete method
void sleep() {
System.out.println("Zzz");
}
}
class Dog extends Animal {
// Implementation of abstract method
void makeSound() {
System.out.println("Woof");
}
}
class Main {
public static void main(String[] args) {
Dog dog = new Dog();
dog.makeSound(); // Outputs "Woof"
dog.sleep(); // Outputs "Zzz"
}
}
In the above code, Animal is an abstract class with one abstract method makeSound and one concrete method sleep. The Dog class is an extended class of Animal that implements the makeSound method.
Let’s break the above code into three parts and understand each of them –
- Abstract Class
- Concrete Class
- Main method
Abstract Class
abstract class Animal {
// Abstract method (no body)
abstract void makeSound();
// Concrete method
void sleep() {
System.out.println("Zzz");
}
}
The above code is just the abstract class part of the whole code meaning that part of the code where we have initialised the abstract class using the abstract keyword as without it the abstract class cannot be initialised.
Let’s understand the above code step by step.
- abstract class Animal: declares an abstract class named Animal using the abstract keyword.
- abstract void make Sound();: This is an abstract method. It has nobody (no implementation). Any class that extends Animal must provide an implementation for this method. Abstract methods are like contracts that subclasses must fulfil.
- void sleep(): This is a concrete method with an implementation. It prints “Zzz” when called. Subclasses can use this method as it is or override it if they need different behavior.
Concrete Subclass
class Dog extends Animal {
// Implementation of abstract method
void makeSound() {
System.out.println("Woof");
}
}
Similar to the Abstract Class part, we will also understand this section step by step
- class Dog extends Animal: This declares a subclass named Dog that extends the abstract class Animal. Since Animal is abstract and has an abstract method, Dog must implement the makeSound method.
- void makeSound(): This provides the implementation for the makeSound method declared in Animal. When makeSound is called on a Dog object, it prints “Woof”.
Main Method
class Main {
public static void main(String[] args) {
Dog dog = new Dog();
dog.makeSound(); // Outputs "Woof"
dog.sleep(); // Outputs "Zzz"
}
}
- class Main: This is a class containing the main method, which is the entry point of the program.
- public static void main(String[] args): This is the main method where the program execution starts.
- Dog dog = new Dog();: This creates a new instance of the Dog class. Even though Animal is abstract and cannot be instantiated, Dog is a concrete class and can be instantiated.
- dog.makeSound();: This calls the makeSound method on the dog object. Since Dog provides an implementation for makeSound, it prints “Woof”.
- dog.sleep();: This calls the sleep method on the dog object. The sleep method is inherited from the Animal class and prints “Zzz”.
Now that we have understood each part of the code separately, it is time to understand the code as a whole –
First things first, Abstraction helps developers in many ways. It not only reduces code complexity, but it also increases code reusability, maintainability and flexibility, and we will see how with this example.
In the above code, we have initiated the abstract class Animal using the abstract keyword and abstract and concrete methods makeSound() and sleep() respectively. We know that the Animal – the abstract class, cannot be instantiated directly and needs a separate class to extend its reach. This is where the Dog class comes into play. It extends the Animal class and provides a specific implementation for the makeSound() method, fulfilling the contract established by the abstract method in Animal. The concrete method sleep() in the Animal class is inherited by the Dog class without modification.
As for the main() method, it serves as the entry point of the program, where an instance of the Dog class is created, and its makeSound() and sleep() methods are called. The makeSound() method prints “Woof,” showcasing the subclass-specific behavior, while the sleep() method prints “Zzz,” demonstrating the inheritance of the default behavior from the abstract class.
This code illustrates how abstract classes and methods provide a framework for defining common functionality while allowing subclasses to provide specific implementations, promoting code reuse and flexibility.
Abstract Method
In the above code, we have seen how can we use abstract class which uses both abstract and concrete methods to extend their reach or what they can do. Like the Animal class extends itself to the Dog subclass which makes Woof sound when the function is triggered and Zzzz when sleep is triggered.
So we have used two types of methods in the above code – Abstract Methods and Concrete Methods. Let’s understand the difference between them.
Abstract Methods | Concrete Methods |
Declared without a body | Declared with a body |
Must be overridden by a concrete subclass | Can be inherited by a subclass |
Used to enforce a contract that a subclass must follow | Provides a common behaviour that can be shared across all subclasses |
Must be implemented by any subclass of an abstract class | These can be inherited and used directly by subclasses. |
For now, let’s understand the Abstract Method only. So what is the Abstract Method?
An abstract method is a method that is declared without an implementation or a body. We can also say that it acts as a placeholder for the methods that must be implemented in subclasses. The things that they contain are their name, return type and parameters.
abstract class ClassName {
abstract returnType methodName(parameters);
}
If we compare the above general code of the Abstract method with the Abstract class Animal code that we have also discussed above, this might become clearer.
abstract void makeSound();
On comparing the two codes, we can say –
Return type is void
Name is makeSound()
Parameters are none
So, we can conclude the heading by saying that just like abstract class abstract method also serves as the blueprint for the methods that subclasses must implement. As told above, abstract methods serve as the placeholder (name, return type and parameters) for the methods that must be implemented in subclasses with no body or implementation.
Key points to remember
- It is declared in an abstract class.
- It has no body.
- Must be overridden, thus cannot be static or final
- Enforces a specific behaviour on all sub-classes.
Interface
We already know that there are two ways by which we can implement abstraction in Java – Abstract classes and interface. We have already understood what Abstract classes are with the help of both theory and code, so let’s understand the concept of interface.
Interface is probably the most powerful tool to achieve abstraction in Java as using interface we can achieve 100% abstraction in Java. Its basic use is to specify what methods a class should implement without dictating how they should be implemented.
Simply said, an interface is a reference type, similar to a class, and all the methods declared in an interface are abstract by default and have no implementation. We will use the word interface just like we used to write the word abstract for abstract classes. Thus it’s safe to say that the interface cannot also be instantiated.
Why do we need an Interface?
- To achieve complete abstraction
- To make code more flexible and maintainable.
- To promote Loose coupling
- To achieve multiple inheritance-like behavior
To better understand interfaces, we’ll use the same code example as we did for abstract classes and methods. This comparison will help clarify how interfaces are both similar to and different from abstract classes.
public interface Animal {
void makeSound(); // abstract method
void sleep(); // abstract method
}
Notice something different in the above code – we have defined methods without the abstract keyword, but they aren’t concrete methods, they are abstract methods. That is because every method defined in an interface is abstract by default thus, it doesn’t require the keyword to be declared abstract.
Now, we also know that an abstract class can extend only once but that’s not the case with the interface. In interface, an abstract class can have multiple interfaces, allowing it to inherit abstract methods from all the interfaces it implements hence, achieving the benefits of multiple inheritance without the associated complexities.
The advantages of interfaces go further, solving one of OOP’s biggest issues, especially in C++: the diamond problem. This occurs when a class hierarchy forms a diamond shape, with a child class inheriting methods and properties from two parent classes that share a common ancestor. Look at the image given below to understand things better.
This can cause confusion because the subclass could inherit conflicting methods or properties. But Java does not allow classes to have multiple inheritance, it only allows interfaces to have them which solves the problem.
Why It Works:
- Interfaces Only Declare Methods: Interfaces declare methods but do not provide implementations. So, there is no conflict or ambiguity about which implementation to use.
- Single Implementation in Subclass: The implementing class provides its implementation of the methods, resolving any potential conflicts.
Before moving further and understanding the interface with the help of code, let’s first see its key points which will give us a much deeper knowledge of this vast topic.
Key Points
- A class can implement multiple interfaces thus making multiple inheritances possible without complexities.
- The interface can only be declared as public or default modifiers.
- Every interface in Java is by default abstract
interface A { } === abstract interface A { }
- Every variable of an interface is either final, static or public while every method is public or abstract.
- In an interface, declaration and initialisation must be done at the same time.
interface A {
// int id; // Compilation error
int id = 20;
}
- An interface can extend another interface but cannot implement it, only classes can.
- If a class implements two interfaces that have the same method name and return type, then implementing them once is enough.
interface A {
int calulateArea();
}
interface B {
int calulateArea();
}
class MyClass implements A, B {
public int calulateArea() {
// Defining once is enough.
}
}
- But if even one of them is different, then it’s not possible and they will be written separately.
interface A {
int calulateArea();
}
interface B {
void calulateArea();
}
class MyClass implements A, B {
// Not possible to define calculateArea() for both interfaces
}
- If by any chance we have an interface and an implementing class with an assigned object, we jiican only call the method that was declared inside the interface. Failure to do so will result in a compilation error.
interface MyInterface {
void print();
}
class MyClass implements MyInterface {
public void print() {
System.out.println("Inside print method");
}
public void message() {
System.out.println("Inside message method");
}
public static void main(String args []) {
MyInterface obj = new MyClass(); // Assigning MyClass object into MyInterface type
// obj.message(); // Compilation error
obj.print(); // prints "Inside print method"
}
}
This approach promotes code modularity, flexibility, and maintainability. It allows us to write code that works with objects of different classes as long as they implement the same interface, without needing to know the specific implementation details of each class.
- Suppose we encounter a situation in which we have defined a variable with the same name in two different interfaces. In that case, we cannot implement it as it will give us a compilation error (the diamond problem). What we can do is use the interface name and the variable name in conjunction. See the below code
interface X {
int id = 10;
}
interface Y {
int id = 20;
}
class MyClass implements A, B {
public static void main(String args []) {
// System.out.println(id); // compilation error
System.out.println(X.id); // Access interface A variable, prints 10
System.out.println(Y.id); // Access interface B variable, prints 20
}
}
- Class implementing an interface cannot change the value of a variable declared in the interface since they are declared as final by default.
interface A {
int id = 35;
void test()
}
class Myclass implements A {
public void test() {
id = 30; // Compilation error
}
}
Understanding Interface with the help of code
Let’s use a different example than the one we used above – Animal class to understand the interface as a whole. For this purpose, we have considered a vehicle whose max speed is 200kmph.
public interface Vehicle {
// Constants
double MAX_SPEED = 200.0; // public, static, and final by default
// Abstract methods
void start();
void accelerate(double speed);
void brake();
double getCurrentSpeed();
}
In the above code, we have defined an interface vehicle, a constant MAX_SPEED which tells us the max speed of the vehicle which is 200kmph and some abstract methods that are directly related to speed like start, accelerate and brake and one more method to calculate the current speed of the vehicle.
The Implementation Part
public class Car implements Vehicle {
private double currentSpeed;
@Override
public void start() {
System.out.println("Car started.");
}
@Override
public void accelerate(double speed) {
currentSpeed += speed;
System.out.println("Car accelerated to " + currentSpeed + " km/h.");
}
@Override
public void brake() {
currentSpeed = 0;
System.out.println("Car stopped.");
}
@Override
public double getCurrentSpeed() {
return currentSpeed;
}
}
In the above code, we have a class named car that implements our vehicle interface and a variable – currentSpeed that keeps track of the car’s current speed and is of the type double as we may have to encounter some decimal values along the way
The abstract method start() tells us that the car has started and now is on the move and the same will be printed when called, the accelerate abstract method on the other hand gives us a value of the car’s speed.
In the above code where we have initialised and declared the interface and the abstract methods, we have also declared the parameter speed of type double as speed can be a decimal value (say 30.5 km/h). When this speed is added to the private field – currentSpeed, we will get our acceleration using the formula
currentSpeed += speed
As for the break method, it’s simple. Normally when we apply brakes, the vehicle comes to a halt thus the current speed of the vehicle becomes 0. Similarly, the currentSpeed of the Car becomes 0 when the break method is called. And then finally the variable currentSpeed is printed as the method getCurrentSpeed is called.
The Main Method
public class Main {
public static void main(String[] args) {
Vehicle myCar = new Car();
myCar.start();
myCar.accelerate(50.0);
System.out.println("Current speed: " + myCar.getCurrentSpeed() + " km/h");
myCar.brake();
}
}
In the main method, we add values to check if our code works as we planned it or are there are bugs. We set a value for the `speed` variable to update the variable `currentSpeed`. We have also created an object `myCar` to call these methods from the `Vehicle` interface, implemented by the `Car` class.
The output of this program would be:
Car started.
Car accelerated to 50.0 km/h.
Current speed: 50.0 km/h
Car stopped.
This example demonstrates how interfaces can be used to define a common contract for different classes, allowing them to communicate with each other using a common set of methods, while still allowing each class to provide its implementation details.
Nested Interface
A nested interface or a member interface which occurs when an interface is declared inside a class or another interface. Though in this blog we will not discuss this topic in much detail, let’s get a basic understanding of it at least.
A nested interface as told above is a way of declaring an interface inside another interface or class. This allows us to logically group interfaces that are only used in one place resulting in increasing encapsulation and reducing clutter.
Though they can only be public or static when declared under an interface and public, default and protected but not private when declared under a class.
Syntax
- Interface inside another interface
interface first{
interface second{
// body
}
}
- Interface inside a Class
class abc{
interface _interface_name{
// body
}
}
As already explained above there are only two ways in which we can initialize abstraction in Java – Abstract Classes and Interface. We have already understood both of them separately in detail using both theory and code. Let’s now see what are some differences and similarities in both ways.
Difference between Abstract class and interface
Parameter | Abstract Class | Interface |
Declaration | Using Abstract keyword | Using Interface keyword |
Method | Both Abstract and Concrete Methods | Abstract, Default and Static Methods (after Java 8) |
Inheritance | Doesn’t support multiple inheritance | Supports multiple inheritance |
Implementation | Can provide for interface | Can not provide for Abstract Class |
Variable types | Can have instance variables, static variables, and constants | Can only have static and final variables (constants) |
Access Modifiers | Have public, protected and private | All methods are public by default, variables are public static final by default |
Abstraction | Partial Abstraction | Complete Abstraction |
Can extend | Another Java Class | Java interface |
Similarity between Abstract Class and Interface
- Both Abstract Classes and Interfaces cannot be instantiated – you cannot create objects of them directly.
- Both can contain abstract methods that have no implementation.
When to use Abstract Class VS When to use interface
According to Oracle documentation, we can use Abstract Class when –
- We want to share code among several closely related classes.
- We expect that classes that extend your abstract class have many common methods or fields, or require access modifiers other than public (such as protected and private).
- We want to declare non-static or non-final fields. This enables you to define methods that can access and modify the state of the object to which they belong.
And interface when
- We expect that unrelated classes will implement your interface. For example, the interfaces Comparable and Cloneable are implemented by many unrelated classes.
- We want to specify the behavior of a particular data type but are not concerned about who implements its behavior.
- We want to take advantage of multiple inheritance of type.
Advantages of Abstraction in Java
- It reduces code complexity and increases code readability and security.
- It also improves maintainability, modularity, reusability and flexibility
- Supports modularity, as complex systems can be divided into smaller and more manageable parts.
- Abstraction allows for flexibility in the implementation of a program, as changes to the underlying implementation details can be made without affecting the user-facing interface.
- Abstraction enables modularity and separation of concerns, making code more maintainable and easier to debug.
Disadvantages of Abstraction in Java
- It can increase the complexity of the code if not used properly.
- It can make thighs difficult to understand particularly the workings of the system.
- This may limit the flexibility of the application.
- Overuse of abstraction can result in decreased performance due to the additional layers of code and indirection.
Conclusion
In conclusion, we would just say that abstraction is one of the four fundamental pillars of the OOPS in Java which helps in hiding the complex part of the system from the user while showing only the essential things to the user which he needs. We can also say that Abstraction is a process that displays what something does while hiding how it’s done.
There are two ways of ways in which we can achieve abstraction – through Abstract classes and Interfaces. Both of these methods can’t be instantiated on their own and need separate keywords to do so. In the case of Abstract Classes, it’s abstract while in the case of interface, it’s interface. We have already discussed about them in detail in the blog, hope you will like it
Abstraction in Java FAQ’s
Q2. What is the difference between Encapsulation and Abstraction
Ans. Encapsulation is the practice of bundling data and methods within a single unit, like a class, and controlling their access, whereas abstraction is about hiding complex implementation details and exposing only the essential functionalities.
Q3. What is the benefit of using abstract Abstract classes?
Ans. It reduces code complexity and increases code readability and security.
Q4. Can we create an object of the Abstract class?
Ans. No, we cannot create an object of an abstract class directly. We can only create objects of subclasses that extend the abstract class and provide implementations for its abstract methods.
Q5. Can an abstract class have a constructor?
Ans. Yes, an abstract class can have a constructor. The constructor is called when an instance of a subclass is created. This is useful for initializing common properties that are shared among all subclasses.
Q6. Can an abstract class be final in Java?
Ans. No, an abstract class cannot be declared as final in Java because a final class cannot be subclassed, and an abstract class needs to be subclassed to provide implementations for its abstract methods.
Q7. Why use interfaces when we have abstract classes?
Ans. The reason is that abstract classes may contain non-final variables, whereas variables in the interface are final, public, and static.
The level of my appreciation for your work mirrors your own enthusiasm. Your sketch is visually appealing, and your authored material is impressive. Yet, you appear to be anxious about the possibility of moving in a direction that may cause unease. I agree that you’ll be able to address this matter efficiently.