Topic 5: Abstraction in Java
📖 7 min read · 🎯 beginner · 🧭 Prerequisites: android-application-structure-folder-structure, operators-in-java
Why this matters
Here's the thing — when you start building Android apps, you quickly run into a problem: you have many different objects that all need to do the same kind of thing, but each one does it differently. A Circle draws itself differently than a Rectangle. A CreditCardPayment works differently than a UPIPayment. So how do you write code that handles all of them without repeating yourself? That's exactly what abstraction solves in Java — you define what needs to happen without locking down how it happens. Abstract classes and interfaces are the tools for this, and once you get comfortable with them, your Android code becomes dramatically cleaner.
What You'll Learn
- What abstraction means in Java and why it matters for Android development
- How to define and use abstract classes with abstract methods
- How to define and implement interfaces to enforce a strict behavioral contract
- How to wire abstract types and interfaces together inside an Android
Activity
The Analogy
Think of a magical library where every book — fiction, non-fiction, reference — shares a common card-catalog entry with standard fields like title, author, and "calculate reading time." The catalog entry is the abstract contract: it tells every librarian what information a book exposes without dictating whether the reading-time formula counts pages, chapters, or word count. Each book type fills in that formula its own way. You, the visitor, interact with the catalog's common interface without needing to care about the mechanics hiding behind each shelf.
Chapter 1: Understanding Abstraction
Abstraction lets you represent complex real-world scenarios with simplified models. Instead of exposing every internal detail of a class, you surface only the behaviors that matter to the outside world and leave the how to the concrete implementations.
In Java, abstraction is achieved through two constructs:
- Abstract classes — declare some or all methods without an implementation; subclasses must fill in the blanks.
- Interfaces — declare method signatures only (no implementation), enforcing a strict contract on any class that claims to implement them.
Together, these tools let you define common behaviors shared across many classes while keeping implementation details flexible and encapsulated.
classDiagram
class Shape {
<<abstract>>
+calculateArea() double
}
class Circle {
-double radius
+calculateArea() double
+draw()
}
class Rectangle {
-double length
-double width
+calculateArea() double
+draw()
}
class Drawable {
<<interface>>
+draw()
}
Shape <|-- Circle
Shape <|-- Rectangle
Drawable <|.. Circle
Drawable <|.. Rectangle
Chapter 2: Abstraction with Abstract Classes
An abstract class is declared with the abstract keyword. It can contain a mix of fully implemented methods and abstract methods — methods with no body that subclasses are required to override.
1. Abstract Class: Shape
// Abstract class Shape
public abstract class Shape {
// Abstract method — subclasses decide the formula
public abstract double calculateArea();
}
Shape cannot be instantiated directly. It is a blueprint. Any class that extends Shape must provide a concrete calculateArea() implementation or be abstract itself.
2. Subclass: Circle
// Subclass Circle
public class Circle extends Shape {
private double radius;
public Circle(double radius) {
this.radius = radius;
}
@Override
public double calculateArea() {
return Math.PI * radius * radius;
}
}
3. Subclass: Rectangle
// Subclass Rectangle
public class Rectangle extends Shape {
private double length;
private double width;
public Rectangle(double length, double width) {
this.length = length;
this.width = width;
}
@Override
public double calculateArea() {
return length * width;
}
}
Both subclasses satisfy the abstract contract: they provide their own calculateArea() formula while the calling code only needs to know about Shape.
Chapter 3: Using Abstract Classes in an Android Activity
Now plug the abstract hierarchy into a real Android screen. The MainActivity creates instances of Circle and Rectangle, calls calculateArea() on each through the Shape reference, and displays the results in a TextView.
1. Activity Code
public class MainActivity extends AppCompatActivity {
private TextView resultTextView;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
resultTextView = findViewById(R.id.result_text_view);
// Create instances of Circle and Rectangle
Shape circle = new Circle(5.0);
Shape rectangle = new Rectangle(4.0, 6.0);
// Calculate the areas
double circleArea = circle.calculateArea();
double rectangleArea = rectangle.calculateArea();
// Display the results
String result = "Circle Area: " + circleArea + "\nRectangle Area: " + rectangleArea;
resultTextView.setText(result);
}
}
2. Layout XML
<!-- activity_main.xml -->
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".MainActivity">
<TextView
android:id="@+id/result_text_view"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerInParent="true"
android:text="Results"
android:textSize="18sp" />
</RelativeLayout>
Notice that MainActivity declares its variables as type Shape — it never needs to know whether it's dealing with a circle or a rectangle to call calculateArea(). That is abstraction paying off in practice.
Chapter 4: Abstraction with Interfaces
While abstract classes can contain partial implementations, interfaces can only contain method signatures — no method bodies at all (in the classic Java sense). This makes interfaces the strictest form of contract: any class that claims to implement an interface must provide every method listed.
1. Interface: Drawable
// Interface Drawable
public interface Drawable {
void draw();
}
A class can extend only one abstract class, but it can implement multiple interfaces, making interfaces the preferred tool for defining cross-cutting capabilities.
2. Implementing Drawable in Circle and Rectangle
Both classes now extend Shape and implement Drawable, satisfying two separate contracts at once:
// Implementing Drawable in Circle
public class Circle extends Shape implements Drawable {
private double radius;
public Circle(double radius) {
this.radius = radius;
}
@Override
public double calculateArea() {
return Math.PI * radius * radius;
}
@Override
public void draw() {
System.out.println("Drawing a circle with radius: " + radius);
}
}
// Implementing Drawable in Rectangle
public class Rectangle extends Shape implements Drawable {
private double length;
private double width;
public Rectangle(double length, double width) {
this.length = length;
this.width = width;
}
@Override
public double calculateArea() {
return length * width;
}
@Override
public void draw() {
System.out.println("Drawing a rectangle with length: " + length + " and width: " + width);
}
}
3. Using the Drawable Interface in an Activity
public class MainActivity extends AppCompatActivity {
private TextView resultTextView;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
resultTextView = findViewById(R.id.result_text_view);
// Create concrete instances
Circle circle = new Circle(5.0);
Rectangle rectangle = new Rectangle(4.0, 6.0);
// Draw the shapes (Drawable contract)
circle.draw();
rectangle.draw();
// Calculate the areas (Shape contract)
double circleArea = circle.calculateArea();
double rectangleArea = rectangle.calculateArea();
// Display the results
String result = "Circle Area: " + circleArea + "\nRectangle Area: " + rectangleArea;
resultTextView.setText(result);
}
}
Calling circle.draw() and rectangle.draw() sends output like:
Drawing a circle with radius: 5.0
Drawing a rectangle with length: 4.0 and width: 6.0
to Logcat, while the area results appear on screen via the TextView.
🧪 Try It Yourself
Task: Add a third shape — Triangle — to the hierarchy.
- Create a
Triangleclass that extendsShapeand implementsDrawable. - Give it three sides (
a,b,c) and compute the area using Heron's formula:s = (a + b + c) / 2area = Math.sqrt(s * (s-a) * (s-b) * (s-c))
- Implement
draw()to print"Drawing a triangle with sides: a, b, c". - Instantiate it in
MainActivityand add its area to the result string.
Success criterion: The screen should display three lines — Circle Area, Rectangle Area, and Triangle Area — without any changes to the Shape abstract class or the Drawable interface.
Starter snippet:
public class Triangle extends Shape implements Drawable {
private double a, b, c;
public Triangle(double a, double b, double c) {
this.a = a; this.b = b; this.c = c;
}
@Override
public double calculateArea() {
double s = (a + b + c) / 2;
return Math.sqrt(s * (s - a) * (s - b) * (s - c));
}
@Override
public void draw() {
// TODO: print the triangle description
}
}
🔍 Checkpoint Quiz
Q1. What is the key difference between an abstract class and an interface in Java?
A) Abstract classes can be instantiated; interfaces cannot. B) Abstract classes can contain method implementations; classic interfaces contain only method signatures. C) Interfaces support inheritance; abstract classes do not. D) There is no difference — they are interchangeable.
Q2. Given this snippet, what is printed to the console when circle.draw() is called with radius = 5.0?
Circle circle = new Circle(5.0);
circle.draw();
A) Drawing a circle with radius: 5
B) Drawing a circle with radius: 5.0
C) Circle area: 78.53981633974483
D) Nothing — draw() returns void and prints nothing.
Q3. What happens if a concrete class declares implements Drawable but does not provide a draw() method body?
A) Java silently ignores the missing method.
B) The class inherits draw() from Object.
C) The code fails to compile with an error about unimplemented abstract methods.
D) The app compiles but crashes at runtime when draw() is called.
Q4. How would you update MainActivity to call draw() on every shape without knowing the concrete type at the call site?
A) Declare the variable as Shape shape = new Circle(5.0); and call shape.draw().
B) Declare the variable as Drawable drawable = new Circle(5.0); and call drawable.draw().
C) Cast every shape to Object before calling draw().
D) You cannot call draw() without knowing the concrete type.
A1. B — Abstract classes may contain fully-implemented methods alongside abstract ones; classic interfaces hold only signatures (no bodies), enforcing a pure contract.
A2. B — The draw() implementation prints "Drawing a circle with radius: " + radius, and radius is a double, so Java renders it as 5.0.
A3. C — The Java compiler enforces interface contracts at compile time. A non-abstract class that fails to implement every interface method will not compile.
A4. B — Declaring the variable as Drawable drawable lets you call draw() through the interface reference without knowing or caring that it's a Circle underneath. Option A would fail because Shape does not declare draw().
🪞 Recap
- Abstraction hides implementation details and exposes only the behaviors a caller needs to know about.
- Abstract classes define a partial blueprint — some methods implemented, some left for subclasses to fill in with
@Override. - Interfaces define a strict contract of method signatures; any implementing class must provide every method body.
- A Java class can extend only one abstract class but can implement multiple interfaces simultaneously.
- In Android, abstract hierarchies flow naturally into
Activitycode: declare variables by the abstract type and call its methods without caring which concrete subclass is behind the reference.
📚 Further Reading
- Java Abstract Classes — Oracle Docs — the source of truth on abstract class rules and constraints
- Java Interfaces — Oracle Docs — full reference on interface syntax, default methods, and multiple implementation
- Effective Java, Item 20: Prefer interfaces to abstract classes — Joshua Bloch's canonical advice on when to reach for each tool
- ⬅️ Previous: Operators in Java
- ➡️ Next: Android Building Blocks I: Activity