C
JV

C to Java

10 lessons

Progress0%
1Introduction to Java2Data Types3Strings4Arrays and Collections5Object-Oriented Programming6Exception Handling7Collections and Generics8Modern Java Features9Interfaces and Polymorphism10Threads and Concurrency
All Mirror Courses
C
JV
Interfaces and Polymorphism
MirrorLesson 9 of 10
Lesson 9

Interfaces and Polymorphism

Java interfaces replace C function pointers and void* for polymorphism; runtime dispatch through vtables vs C manual switch/cast patterns.

Introduction

In this lesson, you'll learn about interfaces and polymorphism in Java. Coming from C, you already have a foundation for understanding this concept. We'll build on that knowledge while highlighting the key differences.

Mirror Card
C
From C:

In C, you're familiar with java interfaces replace c function pointers and void* for polymorphism; runtime dispatch through vtables vs c manual switch/cast patterns..

JV
In Java:

Java has its own approach to java interfaces replace c function pointers and void* for polymorphism; runtime dispatch through vtables vs c manual switch/cast patterns., which we'll explore step by step.

The Java Way

Let's see how Java handles this concept. Here's a typical example:

JV
Java Example
import java.util.List;

// Interface replaces the vtable
interface Shape {
    double area();
    String name();
}

record Circle(double radius) implements Shape {
    public double area() { return Math.PI * radius * radius; }
    public String name() { return "Circle"; }
}

record Rect(double width, double height) implements Shape {
    public double area() { return width * height; }
    public String name() { return "Rect"; }
}

public class Shapes {
    public static void main(String[] args) {
        List<Shape> shapes = List.of(
            new Circle(5.0),
            new Rect(4.0, 6.0)
        );
        for (Shape s : shapes) {
            System.out.printf("%s area: %.2f%n", s.name(), s.area());
        }
        // Dynamic dispatch handled by JVM — no manual vtable needed
    }
}

Comparing to C

Here's how you might have written similar code in C:

C
C (What you know)
#include <stdio.h>
#include <math.h>

typedef struct {
    double (*area)(const void *self);  /* function pointer */
    const char *name;
} ShapeVtable;

typedef struct { const ShapeVtable *vtable; double radius; } Circle;
typedef struct { const ShapeVtable *vtable; double w, h; } Rect;

double circle_area(const void *s) {
    const Circle *c = s; return M_PI * c->radius * c->radius;
}
double rect_area(const void *s) {
    const Rect *r = s; return r->w * r->h;
}

static const ShapeVtable circle_vt = { circle_area, "Circle" };
static const ShapeVtable rect_vt   = { rect_area,   "Rect" };

int main(void) {
    Circle c = { &circle_vt, 5.0 };
    Rect r   = { &rect_vt, 4.0, 6.0 };
    const ShapeVtable *shapes[] = { c.vtable, r.vtable };
    const void *objs[] = { &c, &r };
    for (int i = 0; i < 2; i++)
        printf("%s area: %.2f\n", shapes[i]->name, shapes[i]->area(objs[i]));
}
Mirror Card
C
From C:

You may be used to different syntax or behavior.

JV
In Java:

Java interface is the idiomatic replacement for C function pointer vtables — no manual casting or void*

Mirror Card
C
From C:

You may be used to different syntax or behavior.

JV
In Java:

implements declares that a class provides all methods in the interface; compiler verifies completeness

Mirror Card
C
From C:

You may be used to different syntax or behavior.

JV
In Java:

Dynamic dispatch is automatic in Java — JVM calls the right implementation based on the runtime type

Mirror Card
C
From C:

You may be used to different syntax or behavior.

JV
In Java:

A class can implement multiple interfaces; it can only extend one class (single inheritance)

Mirror Card
C
From C:

You may be used to different syntax or behavior.

JV
In Java:

instanceof operator checks runtime type; use with pattern matching (instanceof Circle c) since Java 16

Step-by-Step Breakdown

1. Declare an Interface

An interface is a contract: it lists method signatures but no implementation. Any class implementing it must provide all methods.

C
C
typedef struct {
    double (*area)(const void *self);
} ShapeVtable;
JV
Java
interface Shape {
    double area();
    String describe();
}
// No implementation here — just signatures

2. Implement the Interface

'implements' binds the class to the contract. The compiler error-checks that all methods are present and have matching signatures.

C
C
double circle_area(const void *s) {
    const Circle *c = s; return M_PI * c->r * c->r;
}
JV
Java
class Circle implements Shape {
    private final double radius;
    Circle(double r) { this.radius = r; }

    public double area() { return Math.PI * radius * radius; }
    public String describe() { return "Circle r=" + radius; }
}

3. Polymorphic Collections

Declare variables and collections as the interface type. The JVM dispatches to the correct implementation at runtime — no switch-case or cast required.

C
C
const ShapeVtable *shapes[] = { c.vtable, r.vtable };
JV
Java
List<Shape> shapes = List.of(new Circle(5), new Rect(4, 6));
for (Shape s : shapes) {
    System.out.println(s.describe() + ": " + s.area());
}

4. instanceof and Pattern Matching

Use instanceof to check the runtime type. Java 16+ pattern matching binds the cast result in one expression.

C
C
if (shapes[i]->type == CIRCLE) { Circle *c = objs[i]; ... }
JV
Java
void process(Shape s) {
    if (s instanceof Circle c) {
        // c is already a Circle — no cast needed
        System.out.println("Radius: " + c.radius());
    } else if (s instanceof Rect r) {
        System.out.println("Width: " + r.width());
    }
}
Rule of Thumb
Prefer polymorphism (interface methods) over instanceof — reserve instanceof for unavoidable type-specific cases.

Common Mistakes

When coming from C, developers often make these mistakes:

  • Java interface is the idiomatic replacement for C function pointer vtables — no manual casting or void*
  • implements declares that a class provides all methods in the interface; compiler verifies completeness
  • Dynamic dispatch is automatic in Java — JVM calls the right implementation based on the runtime type
Common Pitfall
Don't assume Java works exactly like C. While the concepts may be similar, the syntax and behavior can differ significantly.

Key Takeaways

  • interface lists method signatures; implements connects the class to the contract
  • Dynamic dispatch is automatic — JVM calls the right method at runtime, no manual vtable
  • A class can implement multiple interfaces; use the interface type for variables and parameters
  • instanceof Circle c (Java 16+) checks and binds in one step — no separate cast
Rule of Thumb
The best way to learn is by doing. Try rewriting some of your C code in Java to practice these concepts.
PreviousNext