JV
PY

Java to Python

10 lessons

Progress0%
1Variables & Types2Classes & OOP3Collections4Exception Handling5File I/O6Functional Programming7Duck Typing and Protocols8Python Ecosystem9Type Hints and Static Analysis10Context Managers and Resources
All Mirror Courses
JV
PY
Duck Typing and Protocols
MirrorLesson 7 of 10
Lesson 7

Duck Typing and Protocols

Python's structural typing vs Java's nominal typing — duck typing, ABC, Protocol

Introduction

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

Mirror Card
JV
From Java:

In Java, you're familiar with python's structural typing vs java's nominal typing — duck typing, abc, protocol.

PY
In Python:

Python has its own approach to python's structural typing vs java's nominal typing — duck typing, abc, protocol, which we'll explore step by step.

The Python Way

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

PY
Python Example
from typing import Protocol, runtime_checkable

# Duck typing — no declaration needed
class Circle:
    def draw(self) -> None: print("Circle")
    def resize(self, factor: float) -> None: pass

class Square:
    def draw(self) -> None: print("Square")
    def resize(self, factor: float) -> None: pass

# Both work without declaring any interface
def render(shape) -> None:
    shape.draw()  # works if it has .draw()

render(Circle())
render(Square())
render("hello")  # would fail at runtime: 'str' has no draw()

# Protocol: structural typing with static type checking
@runtime_checkable
class Drawable(Protocol):
    def draw(self) -> None: ...

def render_typed(shape: Drawable) -> None:
    shape.draw()

# Protocols checked by mypy/pyright at development time
isinstance(Circle(), Drawable)  # True — runtime check

# ABC: nominal typing (closer to Java)
from abc import ABC, abstractmethod

class Shape(ABC):
    @abstractmethod
    def area(self) -> float: ...
    # Concrete classes MUST subclass Shape

Comparing to Java

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

JV
Java (What you know)
// Java: nominal typing — must explicitly implement
public interface Drawable {
    void draw();
}

public interface Resizable {
    void resize(double factor);
}

// Must explicitly declare 'implements'
public class Circle implements Drawable, Resizable {
    private double radius;

    @Override
    public void draw() { System.out.println("Circle"); }

    @Override
    public void resize(double factor) { radius *= factor; }
}

// Only Circle (and declared implementors) can be used as Drawable
void render(Drawable d) { d.draw(); }
render(new Circle()); // OK
// render("string");  // Compile error — String doesn't implement Drawable
Mirror Card
JV
From Java:

You may be used to different syntax or behavior.

PY
In Python:

Python duck typing: if it has the method, it works — no interface declaration needed

Mirror Card
JV
From Java:

You may be used to different syntax or behavior.

PY
In Python:

Protocol (Python 3.8+) adds structural typing for static checkers without runtime obligation

Mirror Card
JV
From Java:

You may be used to different syntax or behavior.

PY
In Python:

ABC is nominal like Java — classes must explicitly subclass

Mirror Card
JV
From Java:

You may be used to different syntax or behavior.

PY
In Python:

isinstance(obj, Protocol) with @runtime_checkable checks structural compatibility at runtime

Mirror Card
JV
From Java:

You may be used to different syntax or behavior.

PY
In Python:

Python's flexibility is powerful but shifts errors to runtime; Java catches at compile time

Step-by-Step Breakdown

1. Duck Typing

Python asks 'does it have the method?' not 'is it declared to have the method?'. Any object with draw() can be passed to render().

JV
Java
// Java: must implement Drawable
class Circle implements Drawable { void draw() {...} }
PY
Python
# Python: just have the method
class Circle:
    def draw(self): print("circle")

def render(shape): shape.draw()  # works for anything with draw()

2. Protocol for Static Checking

Protocol enables type checkers (mypy/pyright) to verify structural compatibility at development time — best of both worlds.

JV
Java
interface Drawable { void draw(); }
PY
Python
class Drawable(Protocol):
    def draw(self) -> None: ...

def render(shape: Drawable) -> None:
    shape.draw()
# mypy/pyright checks that arg has draw() method

3. ABC for Nominal Typing

ABC (Abstract Base Class) is Python's closest equivalent to Java interfaces — classes must subclass it and implement abstract methods.

JV
Java
interface Shape { double area(); }
public class Circle implements Shape { ... }
PY
Python
from abc import ABC, abstractmethod
class Shape(ABC):
    @abstractmethod
    def area(self) -> float: ...

class Circle(Shape):
    def area(self) -> float: return 3.14 * self.r**2

4. Pragmatic Choice

In production Python, use Protocol for library code (flexible for users), ABC for internal hierarchies (enforces implementation).

JV
Java
// Java: always interfaces for APIs
PY
Python
# Library API: Protocol (flexible, no inheritance required)
class Serializable(Protocol):
    def to_json(self) -> str: ...

# Internal hierarchy: ABC (enforces completeness)
class BaseProcessor(ABC):
    @abstractmethod
    def process(self, data): ...
Rule of Thumb
Use Protocol for public APIs; ABC when you want to enforce subclassing within your codebase.

Common Mistakes

When coming from Java, developers often make these mistakes:

  • Python duck typing: if it has the method, it works — no interface declaration needed
  • Protocol (Python 3.8+) adds structural typing for static checkers without runtime obligation
  • ABC is nominal like Java — classes must explicitly subclass
Common Pitfall
Don't assume Python works exactly like Java. While the concepts may be similar, the syntax and behavior can differ significantly.

Key Takeaways

  • Duck typing: any object with the right methods works — no declaration needed
  • Protocol: structural typing for static checkers — checked by mypy/pyright, not the runtime
  • ABC: nominal typing like Java interfaces — must explicitly subclass
  • Choose Protocol for flexible APIs; ABC when enforcing implementation completeness
Rule of Thumb
The best way to learn is by doing. Try rewriting some of your Java code in Python to practice these concepts.
PreviousNext