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
EasyLet'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 aMessengerinterface with a single methodSend(message string) string. Then create two structs that will implicitly satisfy this interface:EmailMessengerwith anAddressfield (string)SMSMessengerwith aPhoneNumberfield (string)
Sendmethod that returns a string describing the delivery. Neither struct should explicitly declare that it implementsMessenger—Go will figure that out automatically based on the method signatures.main.go: Create a function calledDeliverMessagethat accepts anyMessengerand a message string, then returns the result of callingSend. 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
}
This lesson includes a short quiz. Start the lesson to answer it and track your progress.
All lessons in Object Oriented Programming
1Fundamentals of Go OOP
External FilesGo Workspace & ModulesPackages & ImportsExported vs Unexported NamesIntroduction to OOP in GoStructs as ClassesDefining Methods on StructsPointer vs Value ReceiversStruct InitializationConstructor FunctionsRecap - Simple Calculator4Interfaces
Introduction to InterfacesImplicit ImplementationInterface as ContractEmpty Interface (any)Type AssertionType SwitchInterface CompositionStringer & Error InterfacesRecap - Shape Calculator7Encapsulation
Exported vs Unexported FieldsPackage-Level EncapsulationGetter & Setter MethodsInformation Hiding in GoRecap - Student Records10Generics (Go 1.18+)
Introduction to GenericsType ParametersType ConstraintsGeneric StructsGeneric Methods WorkaroundRecap - Generic Collection2Types & Structs Deep Dive
Basic & Composite TypesCustom Type DefinitionsStruct TagsAnonymous StructsNested StructsZero Values & DefaultsRecap - Contact Book5Composition Over Inheritance
Why Go Has No InheritanceStruct Embedding BasicsMethod PromotionEmbedding Multiple StructsEmbedding vs AggregationShadowing Embedded MethodsRecap - Employee Hierarchy8Error Handling & OOP
The error InterfaceCustom Error TypesError Wrapping (fmt.Errorf)Sentinel Errorserrors.Is() and errors.As()Panic, Defer, and RecoverRecap - File Parser3Pointers & Memory
Pointer Basics in GoPointers to StructsPass by Value vs ReferenceThe new() FunctionGarbage Collection in GoRecap - Linked List Builder6Polymorphism in Go
Polymorphism via InterfacesDuck Typing in GoInterface Satisfaction RulesPolymorphic CollectionsDependency InjectionRecap - Payment Processor9Concurrency & OOP
Goroutines BasicsChannels & CommunicationBuffered vs Unbuffered ChanSelect Statementsync.Mutex & sync.RWMutexsync.WaitGroupThread-Safe Struct DesignRecap - Worker Pool