Skip to main content

Go Ep 9: Implicit Interfaces & Polymorphism

Rachmat Hidayat
Author
Rachmat Hidayat
Learn & sharing insights on TypeScript, Go, Kubernetes, DevOps, DevSecOps, SRE, Platform Engineering, AI/ML Engineering, and MLOps.
go - This article is part of a series.
Part 9: This Article
If it walks like a duck and quacks like a duck, Go treats it like a duck. Interfaces in Go are satisfied implicitly, creating loose coupling and high testability across packages.

TL;DR (Quick Summary)
#

  • Implicit Satisfaction: If a struct implements the methods of an interface, it implements the interface automatically.
  • Interface Composition: Interfaces can embed other interfaces (e.g. io.ReadWriter embeds io.Reader and io.Writer).
  • any (Empty Interface interface{}): Represents a value of any type.
  • Type Assertion (v, ok := i.(T)): Extracts the concrete underlying value safely.

1. Interface Polymorphism Example
#

package main

import "fmt"

// Interface declaration
type Notifier interface {
	Notify(message string) error
}

// Concrete Type 1
type EmailNotifier struct {
	Email string
}

func (e EmailNotifier) Notify(msg string) error {
	fmt.Printf("Sending Email to %s: %s\n", e.Email, msg)
	return nil
}

// Concrete Type 2
type SMSNotifier struct {
	PhoneNumber string
}

func (s SMSNotifier) Notify(msg string) error {
	fmt.Printf("Sending SMS to %s: %s\n", s.PhoneNumber, msg)
	return nil
}

// Polymorphic Function accepting ANY Notifier
func SendAlert(n Notifier, alertMsg string) {
	n.Notify(alertMsg)
}

func main() {
	email := EmailNotifier{Email: "admin@work.com"}
	sms := SMSNotifier{PhoneNumber: "+123456789"}

	SendAlert(email, "Server Disk Full!")
	SendAlert(sms, "CPU Spiked to 99%!")
}

2. Type Assertions & Type Switches
#

package main

import "fmt"

func ProcessValue(val any) {
	// 1. Type Switch
	switch v := val.(type) {
	case int:
		fmt.Printf("Integer multiplied by 2: %d\n", v*2)
	case string:
		fmt.Printf("String length: %d\n", len(v))
	default:
		fmt.Println("Unknown type")
	}

	// 2. Safe Comma-OK Type Assertion
	if str, ok := val.(string); ok {
		fmt.Println("Extracted String:", str)
	}
}

func main() {
	ProcessValue(42)
	ProcessValue("Hello Go")
}

3. Troubleshooting & Common Errors
#

Error 1: Interface Contains Nil Pointer But Is Not Nil
#

The Cause: An interface variable holding a nil pointer struct contains type info, so i == nil evaluates to false!

var p *EmailNotifier = nil
var n Notifier = p

// ⚠️ n is NOT nil because it holds type metadata (*EmailNotifier)!
if n != nil {
    // n.Notify("test") 💥 Panics inside method if it dereferences nil pointer!
}

Summary & Next Steps
#

In this episode:

  • We satisfied Go interfaces implicitly without implements.
  • We wrote polymorphic functions accepting small interfaces.
  • We used comma-ok Type Assertions and Type Switches.

In Episode 10: Idiomatic Error Handling, we will master explicit error returns, %w wrapping, errors.Is, and errors.As!

go - This article is part of a series.
Part 9: This Article