Sitemap
Amit Kumar

Amit Kumar

Software Engineer-1 @Esko | Ex- Bizongo, Samsung, Sirovate, ByteLearn, Bikayi | IIIT Gwalior'23

Java OOP Abstraction & Interfaces

7 min readOct 3, 2026

--

Programming to a Contract

Story: Our application is now getting bigger. We have many implementations, but the rest of the application doesn’t need to know how each implementation works. It only needs to know what each object promises to do.

Press enter or click to view image in full size

1. The Problem

Imagine a payment system.

We have:

UPI
Credit Card
Wallet
Net Banking

Every payment method needs:

pay()

But the implementation is different.

UPI may communicate with a UPI system.

Credit Card may communicate with a card network.

Wallet may communicate with a wallet provider.

The caller shouldn’t need to know all of these details.

The caller simply wants:

Make the payment.

This is the problem abstraction solves.

2. What Is Abstraction?

Abstraction means:

Expose the essential concept or behavior while hiding unnecessary implementation details.

Think about a car.

You interact with:

Steering
Brake
Accelerator
Gear

You don’t need to know:

Engine timing
Fuel injection
ECU logic
Transmission internals

You use a simple interface while the complexity stays hidden.

3. Encapsulation vs Abstraction

Encapsulation

Focus:

Protect and control internal state.

private
public
protected
methods

Abstraction

Focus:

Hide unnecessary complexity and expose essential behavior.

abstract class
interface

Easy Memory Trick

Encapsulation protects the inside.

Abstraction simplifies the outside.

4. Abstract Class

Suppose Payment is a concept rather than a concrete payment method.

We can write:

abstract class Payment {
abstract void pay();
}

The abstract method:

abstract void pay();

means:

A concrete Payment must provide a pay() implementation.

5. Abstract Class Cannot Be Instantiated

This is invalid:

Payment payment = new Payment();

Why?

Because an abstract class represents an incomplete/general abstraction.

But this is valid:

Payment payment = new UPIPayment();

because UPIPayment is concrete.

6. Implementing an Abstract Method

class UPIPayment extends Payment {
@Override
void pay() {
System.out.println("Processing UPI payment");
}
}

Another implementation:

class CardPayment extends Payment {
@Override
void pay() {
System.out.println("Processing card payment");
}
}

Now:

              Payment
/ \
/ \
UPI Card
pay() pay()

7. Abstract Classes Can Have Normal Methods

An abstract class can contain both abstract and concrete methods.

abstract class Payment {
abstract void pay();
void printReceipt() {
System.out.println("Receipt generated");
}
}

A child only needs to implement the abstract method:

class UPI extends Payment {
@Override
void pay() {
System.out.println("UPI payment");
}
}

It automatically has access to:

pay()
printReceipt()

8. Abstract Classes Can Have Fields

abstract class Employee {
protected String name;
protected double salary;
abstract void work();
void printDetails() {
System.out.println(name + " " + salary);
}
}

Abstract classes can therefore provide shared state and shared implementation.

9. Abstract Classes Can Have Constructors

Even though we cannot create:

new Employee();

the abstract class can still have a constructor.

abstract class Employee {
protected String name;
Employee(String name) {
this.name = name;
}
}

When we create:

Developer d = new Developer("Amit");

the parent constructor can run first.

new Developer()
↓
Employee constructor
↓
Developer constructor

10. Abstract Class Mental Model

Think:

Abstract Class
│
├── Shared state
├── Constructor
├── Shared behavior
└── Required behavior
↓
abstract methods

It is a partially implemented base abstraction.

11. Interfaces

Now suppose we don’t need shared state.

We only want to define a contract:

Any Payment must support pay().

An interface is perfect for this.

interface Payment {
void pay(double amount);
}

This defines a contract.

12. Implementing an Interface

class UPIPayment implements Payment {
@Override
public void pay(double amount) {
System.out.println("Paid using UPI");
}
}

The keyword is:

implements

because the class is fulfilling the interface contract.

13. extends vs implements

class Developer extends Employee

means:

Developer inherits from Employee.

class UPI implements Payment

means:

UPI agrees to fulfill the Payment contract.

Remember:

class → extends class
class → implements interface
interface → extends interface

14. Interface as a Contract

Think of:

interface Flyable {
void fly();
}

as saying:

If you claim to be Flyable, you must provide fly().

Then:

class Bird implements Flyable {
@Override
public void fly() {
System.out.println("Bird flying");
}
}

and:

class Airplane implements Flyable {
@Override
public void fly() {
System.out.println("Airplane flying");
}
}

Bird and Airplane don’t need to share the same class hierarchy.

They simply share a capability:

CAN-FLY

15. Interface Polymorphism

Now:

Payment payment = new UPIPayment();

The reference type is:

Payment

The actual object is:

UPIPayment

Then:

payment.pay(1000);

executes:

UPIPayment.pay()

This is polymorphism through an interface.

16. Why Use an Interface Reference?

Compare:

UPIPayment payment = new UPIPayment();

with:

Payment payment = new UPIPayment();

The second says:

I don’t care about the concrete implementation. I care only that this object follows the Payment contract.

This reduces coupling.

17. Programming to an Abstraction

Instead of:

class CheckoutService {
private UPIPayment payment;
}

we can write:

class CheckoutService {
private Payment payment;
CheckoutService(Payment payment) {
this.payment = payment;
}
}

Now the service can work with:

UPI
Card
Wallet
Net Banking

without changing the service itself.

Get Amit Kumar’s stories in your inbox

Join Medium for free to get updates from this writer.

This is called:

Programming to an abstraction/interface rather than a concrete implementation.

18. Loose Coupling

If a class depends directly on:

UPIPayment

it is tightly coupled to that implementation.

If it depends on:

Payment

it depends on an abstraction.

Concrete implementation
↓
Higher coupling
Interface / abstraction
↓
Lower coupling

This idea becomes extremely important in Spring Boot.

19. Multiple Interfaces

Java doesn’t allow:

class C extends A, B

But it allows:

class Duck implements Flyable, Swimmable {
}

Example:

interface Flyable {
void fly();
}

interface Swimmable {
void swim();
}

class Duck implements Flyable, Swimmable {
@Override
public void fly() {
System.out.println("Flying");
}
@Override
public void swim() {
System.out.println("Swimming");
}
}

So one class can have:

one parent class
+
multiple capabilities

20. Interface Inheritance

An interface can extend another interface.

interface Animal {
void eat();
}

interface Pet extends Animal {
void play();
}

Then:

class Dog implements Pet {
@Override
public void eat() {
}

@Override
public void play() {
}
}

The class fulfills both contracts.

21. Abstract Class vs Interface

Abstract Class

Best thought of as:

Shared base abstraction

Can contain:

instance fields
constructors
concrete methods
abstract methods

Example:

Employee
├── name
├── salary
├── printDetails()
└── work()

Interface

Best thought of as:

Contract or capability

Examples:

Payment
Flyable
Runnable
Comparable
Serializable

A class can implement multiple interfaces.

22. Abstract Class vs Interface Table

FeatureAbstract ClassInterfaceAbstract methodsYesYesConcrete methodsYesYes, through modern interface featuresInstance fieldsYesNoConstructorsYesNoInstance stateYesNoMultiple class inheritanceNoA class can implement multiple interfacesClass usesextendsimplementsMain purposeShared base abstractionContract/capability

23. Simple Mental Rule

A useful starting point:

Abstract Class
↓
IS-A
↓
Shared base

Interface:

Interface
↓
CAN-DO
↓
Capability / contract

This isn’t an absolute rule, but it’s a very useful design intuition.

24. Default Methods

Modern Java interfaces can contain default implementations.

interface Payment {
void pay();
default void printReceipt() {
System.out.println("Receipt generated");
}
}

An implementing class:

class UPI implements Payment {
@Override
public void pay() {
System.out.println("UPI payment");
}
}

can use:

UPI payment = new UPI();
payment.pay();
payment.printReceipt();

The class inherits the default implementation.

25. Why Were Default Methods Added?

Imagine an interface already has hundreds of implementations.

If a new abstract method were added:

void printReceipt();

all existing implementations would need to implement it.

Default methods allow interfaces to evolve while providing a default implementation.

26. Static Interface Methods

Interfaces can also contain static methods.

interface Payment {
static void validateAmount(double amount) {
System.out.println("Validating amount");
}
}

Call it through the interface:

Payment.validateAmount(1000);

It belongs to the interface itself.

27. Interface Fields

Interface fields are implicitly:

public
static
final

Example:

interface Payment {
int MAX_RETRIES = 3;
}

Conceptually:

public static final int MAX_RETRIES = 3;

The value cannot be reassigned.

28. The Complete OOP Story

We can now connect everything.

Step 1 — Class

We define the structure and behavior of an object.

Class
↓
Blueprint

Step 2 — Object

We create an actual instance.

Object
↓
Actual thing

Step 3 — Constructor

We initialize the object.

Constructor
↓
Initial state

Step 4 — Encapsulation

We protect internal state.

private
↓
Controlled access

Step 5 — Inheritance

We create specialized types.

Employee
↓
Developer

Step 6 — Overriding

A child provides specialized behavior.

Employee.work()
↓
Developer.work()

Step 7 — Polymorphism

One reference can represent different implementations.

Employee employee = new Developer();

Step 8 — Abstraction

We stop caring about unnecessary implementation details.

Payment
↓
pay()

Step 9 — Interface

We express a contract.

interface Payment {
void pay();
}

Now:

UPI
Card
Wallet

can all implement the same contract.

29. The Complete OOP Mental Model

                          OOP
│
┌─────────────────┼─────────────────┐
↓ ↓ ↓
CLASS OBJECT REFERENCE
│ │ │
Blueprint Instance Points to
│ │ │
└─────────────────┼─────────────────┘
↓
CONSTRUCTOR
│
↓
this
│
↓
ENCAPSULATION
│
↓
private state
│
↓
INHERITANCE
│
↓
IS-A
│
↓
OVERRIDING
│
↓
POLYMORPHISM
│
┌───────────┴───────────┐
↓ ↓
Reference Type Actual Object
↓ ↓
What can I access? Which override runs?
│
└───────────┬───────────┘
↓
ABSTRACTION
│
┌────────┴────────┐
↓ ↓
Abstract Class Interface
↓ ↓
Shared Base Contract
│
↓
Loose Coupling

30. The Most Important Pattern

Whenever you see:

Payment payment = new UPIPayment();

think:

Payment
↓
Abstraction / Contract
UPIPayment
↓
Concrete Implementation
payment
↓
Reference
new UPIPayment()
↓
Actual Object
payment.pay()
↓
Polymorphism
↓
UPIPayment.pay()

This single pattern connects:

Abstraction
+
Interface
+
Polymorphism
+
Loose Coupling

Quick Revision

Abstraction

Hide unnecessary implementation details and expose essential behavior.

Abstract class

Partially implemented base abstraction.

Interface

Contract/capability that classes can implement.

extends

Class inheritance or interface inheritance.

implements

Class fulfills an interface contract.

Default method

Interface method with a default implementation.

Interface field

Implicitly public static final.

Loose coupling

Depending on abstractions rather than concrete implementations.

Programming to an interface

Write code against the contract rather than a specific implementation.

Key mental model

Abstract Class
→ Shared base
Interface
→ Contract / capability
Polymorphism
→ Multiple implementations behind one abstraction

--

--

Amit Kumar
Amit Kumar

Software Engineer-1 @Esko | Ex- Bizongo, Samsung, Sirovate, ByteLearn, Bikayi | IIIT Gwalior'23