Interfaces

Course 2 · Ch 2
Interfaces: Polymorphism Without Classes or Inheritance
Go has no "extends" — types qualify for a shared role just by having the right methods, with no declaration required

Chapter 1 gave Person a method. This chapter asks: how does code write a function that works on any type that has a particular method, without caring exactly what that type is? Go's answer is the interface — and Go's interfaces work in a way that has no real JavaScript equivalent, since JavaScript has no static types to satisfy in the first place.

Defining an Interface

type Shape interface { Area() float64 }

An interface lists method signatures only — no implementation, no fields. Shape says: "anything with an Area() float64 method counts as a Shape." Nothing else about the type matters.

Implementing an Interface — Implicitly

type Circle struct { Radius float64 } func (c Circle) Area() float64 { return 3.14159 * c.Radius * c.Radius } type Rectangle struct { Width, Height float64 } func (r Rectangle) Area() float64 { return r.Width * r.Height }

Neither Circle nor Rectangle mentions Shape anywhere. There's no implements Shape keyword in Go at all — a type satisfies an interface automatically, just by having every method the interface requires, with matching signatures. This is called structural typing: it's about shape, not declared relationship.

No "implements" keyword — this is deliberate
A type can satisfy a brand-new interface written months after the type itself was defined, with zero changes to the original type. This is the core reason Go interfaces are described as "implicit" — the connection only exists at the point something actually requires it.

Writing a Function That Accepts the Interface

func describe(s Shape) { fmt.Printf("Area: %.2f\n", s.Area()) } c := Circle{Radius: 5} r := Rectangle{Width: 4, Height: 6} describe(c) // Area: 78.54 describe(r) // Area: 24.00

describe takes a Shape — it works on Circle, Rectangle, or any future type with an Area() method, all through one function. This is polymorphism: one piece of code, many concrete types, achieved here with no class hierarchy, no extends, and no inheritance chain at all.

A Slice of an Interface Type

shapes := []Shape{c, r} for _, shape := range shapes { fmt.Printf("%.2f\n", shape.Area()) } // 78.54 // 24.00

[]Shape can hold a mix of any concrete types that satisfy ShapeCircle and Rectangle side by side in the same slice, the same loop, the same Area() call. This is the most common real-world use for interfaces: a heterogeneous collection processed uniformly.

The Empty Interface and any

func printAnything(value any) { fmt.Println(value) } printAnything(42) printAnything("hello") printAnything(Circle{Radius: 2})

any (an alias for the older interface{}) is an interface with zero required methods — literally everything satisfies it, since there's nothing to satisfy. This is the closest Go gets to JavaScript's "accepts anything" flexibility, but it's used sparingly: reaching for any too often throws away the type-checking that makes Go, Go.

any isn't a free pass back to JavaScript-style typing
A function receiving any doesn't know what's actually inside without a separate runtime check (a "type assertion" or "type switch," beyond this chapter's scope). Reaching for any as a default habit defeats the purpose of Go's type system — it should be the exception, not the rule.
ConceptJavaScriptGo
Shared behaviour across typesDuck typing — works at runtime if methods existInterfaces — checked at COMPILE time
Declaring a relationshipclass X extends Y / implements ZNothing — satisfied implicitly
"Accepts anything"Default behaviour (no static types)any — explicit opt-out, used sparingly

Coding Challenges

Challenge 1

Define an interface Speaker with a method Speak() string. Define two structs, Dog and Cat, each with a Speak() method returning an appropriate sound. Write a function announce(s Speaker) that prints the result of Speak(), and call it with both a Dog and a Cat.

📄 View solution
Challenge 2

Using the Shape interface, Circle, and Rectangle from this chapter, create a slice []Shape containing at least 3 shapes (mixing circles and rectangles), then loop over it printing each one's area. Add up all the areas into a single total, printed at the end.

📄 View solution
Challenge 3

Write a function printAll(items []any) that takes a slice of any and prints each item on its own line. Call it with a slice containing a mix of an int, a string, and a Circle value.

📄 View solution

Chapter 2 Quick Reference

  • type Name interface { Method() returnType } — lists required method signatures only
  • No "implements" keyword — a type satisfies an interface just by having matching methods
  • Structural typing — the relationship is about shape, never declared explicitly
  • func f(x InterfaceType) — accepts ANY concrete type that satisfies the interface
  • []InterfaceType — a slice that can mix different concrete types together
  • any (interface{}) — zero required methods; satisfied by literally everything
  • Polymorphism in Go — achieved via interfaces, with no classes or inheritance chains
  • Next chapter: goroutines and channels — Go's built-in approach to concurrency