How to Use Interface for Polymorphism in Go
In Go, you use
interface types to achieve polymorphism by defining methods that multiple types implement. You can then write functions that accept the interface type, allowing different concrete types to be used interchangeably through their shared behavior.Syntax
An interface in Go defines a set of method signatures without implementations. Any type that implements all these methods satisfies the interface automatically.
Example parts:
type InterfaceName interface { MethodName() ReturnType }: defines an interface.func (t TypeName) MethodName() ReturnType {}: implements the method for a type.var i InterfaceName = TypeName{}: assigns a concrete type to an interface variable.
go
type Speaker interface { Speak() string } // Any type with Speak() string method satisfies Speaker interface type Dog struct {} func (d Dog) Speak() string { return "Woof!" } func saySomething(s Speaker) { println(s.Speak()) }
Example
This example shows two types implementing the same interface. The saySomething function accepts the interface type and calls the method, demonstrating polymorphism.
go
package main import "fmt" type Speaker interface { Speak() string } type Dog struct {} func (d Dog) Speak() string { return "Woof!" } type Cat struct {} func (c Cat) Speak() string { return "Meow!" } func saySomething(s Speaker) { fmt.Println(s.Speak()) } func main() { d := Dog{} c := Cat{} saySomething(d) // Dog speaks saySomething(c) // Cat speaks }
Output
Woof!
Meow!
Common Pitfalls
Common mistakes when using interfaces for polymorphism in Go include:
- Not implementing all interface methods exactly as defined, causing a type not to satisfy the interface.
- Using pointer receivers vs value receivers inconsistently, which can affect whether a type satisfies an interface.
- Trying to use interfaces without understanding that Go uses implicit implementation, so no explicit declaration is needed.
go
package main import "fmt" type Speaker interface { Speak() string } type Bird struct {} // Wrong: pointer receiver, but interface used with value func (b *Bird) Speak() string { return "Tweet!" } func saySomething(s Speaker) { fmt.Println(s.Speak()) } func main() { b := Bird{} // saySomething(b) // Error: Bird does not implement Speaker (Speak method has pointer receiver) saySomething(&b) // Correct: pass pointer }
Output
Tweet!
Quick Reference
Tips for using interfaces for polymorphism in Go:
- Define interfaces with only the methods you need.
- Any type implementing those methods satisfies the interface automatically.
- Use pointer receivers if your methods modify the receiver or to avoid copying large structs.
- Pass interface types to functions to accept any implementing type.
- Use interfaces to write flexible and testable code.
Key Takeaways
Interfaces in Go define method sets that types implement implicitly for polymorphism.
Functions can accept interface types to work with any concrete type implementing those methods.
Pointer vs value receivers affect whether a type satisfies an interface; be consistent.
No explicit declaration is needed; just implement the methods to satisfy an interface.
Use interfaces to write flexible, reusable, and testable Go code.