Menu
Coddy logo textTech

Implicit Implementation

Part of the Object Oriented Programming section of Coddy's GO journey — lesson 26 of 107.

In many object-oriented languages like Java or C#, you must explicitly declare that a type implements an interface using keywords like implements. Go takes a different approach: interface implementation is implicit.

A type satisfies an interface simply by implementing all of its methods. There's no need to declare the relationship anywhere. The compiler figures it out automatically.

type Writer interface {
    Write(data string) int
}

type FileWriter struct {
    Filename string
}

// FileWriter implicitly implements Writer
func (f FileWriter) Write(data string) int {
    fmt.Println("Writing to", f.Filename)
    return len(data)
}

Notice there's no implements Writer declaration on FileWriter. Because FileWriter has a Write(data string) int method matching the interface signature, it automatically satisfies the Writer interface.

This design has a powerful consequence: you can define interfaces after types already exist. If a third-party library has a type with methods you need, you can create an interface that it already satisfies without modifying the original code.

func Save(w Writer, content string) {
    w.Write(content)
}

func main() {
    fw := FileWriter{Filename: "data.txt"}
    Save(fw, "Hello!")  // Works because FileWriter satisfies Writer
}

Implicit implementation keeps Go code decoupled and flexible. Types don't need to know about interfaces ahead of time, making it easier to compose systems from independent parts.

challenge icon

Challenge

Easy

Let's build a message delivery system that demonstrates how types implicitly satisfy interfaces in Go. You'll create different messenger types that all satisfy the same interface—without ever declaring that relationship explicitly.

You'll organize your code across two files:

  • messengers.go: Define a Messenger interface with a single method Send(message string) string. Then create two structs that will implicitly satisfy this interface:
    • EmailMessenger with an Address field (string)
    • SMSMessenger with a PhoneNumber field (string)
    Each struct should have a Send method that returns a string describing the delivery. Neither struct should explicitly declare that it implements Messenger—Go will figure that out automatically based on the method signatures.
  • main.go: Create a function called DeliverMessage that accepts any Messenger and a message string, then returns the result of calling Send. Read contact information from input, create both messenger types, and use your function to deliver messages through each one.

The following inputs will be provided:

  • Line 1: Email address
  • Line 2: Phone number
  • Line 3: Message to send

Your Send methods should return strings in these formats:

  • EmailMessenger: Email to [Address]: [message]
  • SMSMessenger: SMS to [PhoneNumber]: [message]

For example, given alice@example.com, 555-1234, and Hello!, your output should be:

Email to alice@example.com: Hello!
SMS to 555-1234: Hello!

The key insight here is that your DeliverMessage function accepts any Messenger, and both EmailMessenger and SMSMessenger satisfy that interface simply by having a matching Send method. There's no implements keyword anywhere—Go's implicit implementation handles it all.

Cheat sheet

In Go, interface implementation is implicit. A type satisfies an interface simply by implementing all of its methods—no explicit declaration is needed.

type Writer interface {
    Write(data string) int
}

type FileWriter struct {
    Filename string
}

// FileWriter implicitly implements Writer
func (f FileWriter) Write(data string) int {
    fmt.Println("Writing to", f.Filename)
    return len(data)
}

There's no implements Writer declaration. Because FileWriter has a Write(data string) int method matching the interface signature, it automatically satisfies the Writer interface.

You can use any type that satisfies an interface without the type knowing about the interface:

func Save(w Writer, content string) {
    w.Write(content)
}

func main() {
    fw := FileWriter{Filename: "data.txt"}
    Save(fw, "Hello!")  // Works because FileWriter satisfies Writer
}

This design allows you to define interfaces after types already exist, making code decoupled and flexible.

Try it yourself

package main

import (
	"bufio"
	"fmt"
	"os"
)

// TODO: Create a DeliverMessage function that accepts any Messenger
// and a message string, then returns the result of calling Send

func main() {
	reader := bufio.NewReader(os.Stdin)
	
	// Read email address
	email, _ := reader.ReadString('\n')
	email = email[:len(email)-1]
	
	// Read phone number
	phone, _ := reader.ReadString('\n')
	phone = phone[:len(phone)-1]
	
	// Read message
	message, _ := reader.ReadString('\n')
	if len(message) > 0 && message[len(message)-1] == '\n' {
		message = message[:len(message)-1]
	}
	
	// TODO: Create an EmailMessenger with the email address
	
	// TODO: Create an SMSMessenger with the phone number
	
	// TODO: Use DeliverMessage to send the message through each messenger
	// and print the results
}
quiz iconTest yourself

This lesson includes a short quiz. Start the lesson to answer it and track your progress.

All lessons in Object Oriented Programming