Abstraction and Interfaces in Java: Abstract Classes, Contracts and Runnable Examples

Learn when Java needs an abstract class, when it needs an interface, and how both work together in one runnable charge-calculation program with exact outputs.

KnowledgeGate Team

Exam prep & CS education

Updated 6 Oct 20266 min read

Java provides both abstract classes and interfaces because shared state and cross-type contracts are different design needs. Both can hide implementation details and support polymorphism. BikeDelivery and VanDelivery reuse state and calculation code through an abstract base, while Packaging joins them only through the Chargeable interface.

Abstraction in Java: expose the operation, hide the mechanism

Abstraction presents the operations a caller needs while keeping implementation choices behind them. With item.calculateCharge(), the caller asks for a charge without knowing whether the object uses distance, vehicle type or item count.

An abstract class can hold object state, run a constructor, reuse concrete methods and leave selected methods abstract. An interface defines a contract that unrelated classes can implement. Avoid the inaccurate shortcut that calls these partial and complete abstraction. OOP for Teaching CS Exams: Classes and Inheritance places abstraction beside the other OOP ideas; Coding & DSA Courses for Placements is the broader learning path.

Term

Meaning in this model

Abstraction

Expose an operation while hiding its formula

Abstract class

Non-instantiable base with state and shared code

Abstract method

Method implemented by a subclass

Interface

Operations an implementing type promises

extends

Builds class inheritance

implements

Adopts an interface

Contract

Behaviour callers may depend on

Abstract classes in Java: build the shared Delivery base

abstract class Delivery owns private final fields trackingId and distanceKm. Its constructor rejects a negative distance with IllegalArgumentException("Distance cannot be negative"). The protected final method distanceKm() exposes the validated value to subclasses without exposing the field.

baseCharge() returns 40, while protected abstract int ratePerKm(); leaves one choice open. The shared calculation is baseCharge() + distanceKm() * ratePerKm(). BikeDelivery returns 8; VanDelivery returns 14.

The constructor initialises shared state for subclass objects, although new Delivery(...) is forbidden. Both subclasses call super(...) and reuse validation and calculation, but cannot read the private fields directly.

Interfaces in Java: define one contract for unrelated charge types

Chargeable declares String label(); and int calculateCharge();. They are public contract methods, so implementations must be public. Interfaces may also have default and static methods, but the contract is the important idea here.

Delivery implements Chargeable with final public implementations; its subclasses only choose ratePerKm(). Unrelated Packaging also implements the interface, stores items and chargePerItem, and returns items * chargePerItem.

Bike and van share state and an algorithm. Packaging shares neither, yet all three fit a Chargeable[] because they promise the loop's two operations.

Chargeable interface implemented by abstract class Delivery with its BikeDelivery and VanDelivery subclasses, and separately by Packaging.

Abstraction and interfaces worked example: run one calculation loop

Save the following source as AbstractionDemo.java:

java
interface Chargeable {
    String label();
    int calculateCharge();
}

abstract class Delivery implements Chargeable {
    private final String trackingId;
    private final int distanceKm;

    Delivery(String trackingId, int distanceKm) {
        if (distanceKm < 0) {
            throw new IllegalArgumentException("Distance cannot be negative");
        }
        this.trackingId = trackingId;
        this.distanceKm = distanceKm;
    }

    protected final int distanceKm() {
        return distanceKm;
    }

    protected int baseCharge() {
        return 40;
    }

    protected abstract int ratePerKm();

    @Override
    public final String label() {
        return trackingId;
    }

    @Override
    public final int calculateCharge() {
        return baseCharge() + distanceKm() * ratePerKm();
    }
}

class BikeDelivery extends Delivery {
    BikeDelivery(String trackingId, int distanceKm) {
        super(trackingId, distanceKm);
    }

    @Override
    protected int ratePerKm() {
        return 8;
    }
}

class VanDelivery extends Delivery {
    VanDelivery(String trackingId, int distanceKm) {
        super(trackingId, distanceKm);
    }

    @Override
    protected int ratePerKm() {
        return 14;
    }
}

class Packaging implements Chargeable {
    private final int items;
    private final int chargePerItem;

    Packaging(int items, int chargePerItem) {
        if (items <= 0 || chargePerItem < 0) {
            throw new IllegalArgumentException("Invalid packaging values");
        }
        this.items = items;
        this.chargePerItem = chargePerItem;
    }

    @Override
    public String label() {
        return "Packaging";
    }

    @Override
    public int calculateCharge() {
        return items * chargePerItem;
    }
}

public class AbstractionDemo {
    public static void main(String[] args) {
        Chargeable[] items = {
            new BikeDelivery("KG-101", 12),
            new VanDelivery("KG-102", 12),
            new Packaging(3, 25)
        };

        int total = 0;
        for (Chargeable item : items) {
            int charge = item.calculateCharge();
            System.out.println(item.label() + ": " + charge);
            total += charge;
        }
        System.out.println("Total: " + total);
    }
}

Compile and run it:

Code
javac AbstractionDemo.java
java AbstractionDemo

Trace the values before checking the output:

  1. Bike: 40 + 12 * 8 = 136.

  2. Van: 40 + 12 * 14 = 208.

  3. Packaging: 3 * 25 = 75.

  4. Total: 136 + 208 + 75 = 419.

Code
KG-101: 136
KG-102: 208
Packaging: 75
Total: 419

The Chargeable loop can call only label() and calculateCharge(). It needs no distance, rate or packaging fields because runtime dispatch selects the implementation. The Java Course: Concepts, MCQs & Coding Questions teaches the surrounding sequence.

Runtime trace of the Chargeable array: bike 136, van 208 and packaging 75 adding up to a total of 419.

Abstract class vs interface: choose from the relationship

Choose Delivery because bike and van share validated state and a calculation template. Choose Chargeable because deliveries and packaging need one contract despite different data.

Decision point

Abstract class

Interface

Purpose

Related class family

Cross-type contract

Per-object state

Instance fields allowed

No ordinary instance fields

Constructors

Yes

No

Reusable implementation

Concrete methods

Optional default and static methods

Syntax used by a class

extends

implements

Number adopted by one class

One superclass

Multiple interfaces

Interface constants are shared, not replacements for object state. Packaging extends Delivery would invent a false is-a relationship. Making Chargeable an abstract class would block a class that already has a superclass. The two tools often cooperate instead of competing.

Common abstraction and interface errors in Java

Mistake

What goes wrong

Fix

new Delivery("KG-200", 5)

Abstract class cannot be instantiated

Construct a concrete delivery

Concrete class omits ratePerKm()

Abstract method remains incomplete

Implement it or make the class abstract

Packaging extends Chargeable

Class cannot extend an interface

Use implements

Package-private calculateCharge()

Weaker than public contract access

Use public and @Override

protected abstract int ratePerKm() {}

Abstract method has a body

Use ;, or make it concrete

State in interface fields

Fields become shared constants

Keep state private in classes

Do not recover concrete types with casts or instanceof. A genuinely needed loop operation belongs in Chargeable.

How assessments test abstraction and interfaces

With Chargeable c = new BikeDelivery("KG-201", 5);, c.calculateCharge() prints 80: the inherited algorithm uses the bike rate, so 40 + 5 * 8 = 80. c.ratePerKm() is unavailable because the protected method is outside the interface contract.

Use these checkable exercises:

  1. Add DroneDelivery with rate 10. new DroneDelivery("KG-103", 7) gives 40 + 7 * 10 = 110; the array total becomes 419 + 110 = 529.

  2. Add GiftWrap implements Chargeable for 2 parcels at 35 each. It prints GiftWrap: 70; the total becomes 419 + 70 = 489.

  3. Run new Packaging(0, 25). It must be rejected with the exact message Invalid packaging values.

Be ready to define the contract, locate state, trace dispatch and justify each type. Technical Interview: OS, DBMS, CN & OOP Prep is the broader revision route.

Abstraction and interfaces in Java: the short version

  1. Abstraction exposes needed operations and hides their mechanisms.

  2. Abstract classes combine object state with incomplete behaviour.

  3. Interfaces define contracts across implementations.

  4. extends builds a class hierarchy.

  5. implements adopts a contract.

The total 419 proves that one interface loop combines different mechanisms. Retype the program and change both distances to 9. Predict bike 40 + 9 * 8 = 112, van 40 + 9 * 14 = 166, packaging 75, and total 112 + 166 + 75 = 353, then run it. Add DroneDelivery("KG-103", 7) to the original array and verify 529.

To apply interfaces to collections and data structures, continue with DSA Using Java: Placement Preparation Course. If your fundamentals are uncertain, use the Java course linked after the trace first. Finish by coding both changes without copying the outputs.