Go Interfaces
Defining contracts for behavior in Go
🔌 What are Go Interfaces?
Go interfaces define method signatures that types must implement. They enable polymorphism and loose coupling, allowing different types to be used interchangeably when they satisfy the same interface contract.
// Define an interface
type Speaker interface {
Speak() string
}
// Types that implement Speaker
type Dog struct{}
func (d Dog) Speak() string { return "Woof!" }
type Cat struct{}
func (c Cat) Speak() string { return "Meow!" }
Output:
Woof!
Meow!
Key Interface Concepts
Contract
Defines what methods a type must have
type Writer interface {
Write([]byte) (int, error)
}
Implicit Implementation
No explicit "implements" keyword needed
// Automatically implements Writer
type MyWriter struct{}
Polymorphism
Different types, same interface
var s Speaker = Dog{}
s = Cat{} // Both work
Empty Interface
interface{} accepts any type
var anything interface{}
anything = 42
🔹 Basic Interface Example
Creating and using a simple interface:
type Shape interface {
Area() float64
Perimeter() float64
}
type Rectangle struct {
Width, Height float64
}
func (r Rectangle) Area() float64 {
return r.Width * r.Height
}
func (r Rectangle) Perimeter() float64 {
return 2 * (r.Width + r.Height)
}
type Circle struct {
Radius float64
}
func (c Circle) Area() float64 {
return 3.14159 * c.Radius * c.Radius
}
func (c Circle) Perimeter() float64 {
return 2 * 3.14159 * c.Radius
}
// Function that works with any Shape
func PrintShapeInfo(s Shape) {
fmt.Printf("Area: %.2f, Perimeter: %.2f\n", s.Area(), s.Perimeter())
}
func main() {
rect := Rectangle{Width: 5, Height: 3}
circle := Circle{Radius: 4}
PrintShapeInfo(rect) // Area: 15.00, Perimeter: 16.00
PrintShapeInfo(circle) // Area: 50.27, Perimeter: 25.13
}
Output:
Area: 15.00, Perimeter: 16.00
Area: 50.27, Perimeter: 25.13
🔹 Empty Interface
The empty interface can hold values of any type:
func PrintAnything(value interface{}) {
fmt.Printf("Value: %v, Type: %T\n", value, value)
}
func main() {
PrintAnything(42)
PrintAnything("hello")
PrintAnything([]int{1, 2, 3})
PrintAnything(true)
}
Output:
Value: 42, Type: int
Value: hello, Type: string
Value: [1 2 3], Type: []int
Value: true, Type: bool
🔹 Type Assertions
Extract the underlying value from an interface:
func ProcessValue(value interface{}) {
// Type assertion with ok check
if str, ok := value.(string); ok {
fmt.Printf("String: %s (length: %d)\n", str, len(str))
return
}
if num, ok := value.(int); ok {
fmt.Printf("Integer: %d (doubled: %d)\n", num, num*2)
return
}
fmt.Printf("Unknown type: %T\n", value)
}
func main() {
ProcessValue("hello")
ProcessValue(42)
ProcessValue(3.14)
}
Output:
String: hello (length: 5)
Integer: 42 (doubled: 84)
Unknown type: float64
🔹 Interface Composition
Combine multiple interfaces into one:
type Reader interface {
Read([]byte) (int, error)
}
type Writer interface {
Write([]byte) (int, error)
}
// Composed interface
type ReadWriter interface {
Reader
Writer
}
type File struct {
name string
}
func (f File) Read(data []byte) (int, error) {
fmt.Printf("Reading from %s\n", f.name)
return len(data), nil
}
func (f File) Write(data []byte) (int, error) {
fmt.Printf("Writing to %s\n", f.name)
return len(data), nil
}
func ProcessFile(rw ReadWriter) {
data := make([]byte, 10)
rw.Read(data)
rw.Write(data)
}
func main() {
file := File{name: "example.txt"}
ProcessFile(file)
}
Output:
Reading from example.txt
Writing to example.txt