Interfaces are central to Go’s design. They enable polymorphism and abstraction through implicit implementation. For more context, see Go Installation Guide, Go Ecosystem Overview, Go Best Practices.
Interface Definition
Basic Interface
package main
import "fmt"
// Define interface
type Writer interface {
Write(data string) error
}
// Implement interface
type FileWriter struct {
filename string
}
func (fw *FileWriter) Write(data string) error {
fmt.Printf("Writing to file %s: %s\n", fw.filename, data)
return nil
}
func main() {
var w Writer = &FileWriter{filename: "output.txt"}
w.Write("Hello, Go!")
}
Multiple Methods
package main
import "fmt"
type Reader interface {
Read() (string, error)
}
type Writer interface {
Write(data string) error
}
type ReadWriter interface {
Reader
Writer
}
type File struct {
name string
}
func (f *File) Read() (string, error) {
return fmt.Sprintf("Content of %s", f.name), nil
}
func (f *File) Write(data string) error {
fmt.Printf("Writing to %s: %s\n", f.name, data)
return nil
}
func main() {
var rw ReadWriter = &File{name: "data.txt"}
content, _ := rw.Read()
fmt.Println(content)
rw.Write("New content")
}
Implicit Implementation
Duck Typing
package main
import "fmt"
type Shape interface {
Area() float64
}
type Circle struct {
radius float64
}
func (c Circle) Area() float64 {
return 3.14 * c.radius * c.radius
}
type Rectangle struct {
width, height float64
}
func (r Rectangle) Area() float64 {
return r.width * r.height
}
func printArea(s Shape) {
fmt.Printf("Area: %.2f\n", s.Area())
}
func main() {
circle := Circle{radius: 5}
rect := Rectangle{width: 4, height: 6}
printArea(circle) // Works without explicit implementation
printArea(rect) // Works without explicit implementation
}
Empty Interface
Using interface
package main
import "fmt"
func printValue(v interface{}) {
fmt.Println(v)
}
func main() {
printValue(42)
printValue("hello")
printValue(3.14)
printValue(true)
printValue([]int{1, 2, 3})
}
Type Assertion
package main
import "fmt"
func describe(v interface{}) {
switch val := v.(type) {
case int:
fmt.Printf("Integer: %d\n", val)
case string:
fmt.Printf("String: %s\n", val)
case float64:
fmt.Printf("Float: %.2f\n", val)
default:
fmt.Printf("Unknown type: %T\n", v)
}
}
func main() {
describe(42)
describe("hello")
describe(3.14)
}
Interface Composition
Embedding Interfaces
package main
import "fmt"
type Reader interface {
Read() string
}
type Writer interface {
Write(data string)
}
type ReadWriter interface {
Reader
Writer
}
type Device struct {
data string
}
func (d *Device) Read() string {
return d.data
}
func (d *Device) Write(data string) {
d.data = data
}
func main() {
var rw ReadWriter = &Device{data: "initial"}
fmt.Println(rw.Read())
rw.Write("updated")
fmt.Println(rw.Read())
}
Common Interfaces
Stringer Interface
package main
import "fmt"
type Person struct {
name string
age int
}
func (p Person) String() string {
return fmt.Sprintf("%s (%d years old)", p.name, p.age)
}
func main() {
p := Person{name: "Alice", age: 30}
fmt.Println(p) // Uses String() method
}
Error Interface
package main
import (
"fmt"
)
type CustomError struct {
message string
code int
}
func (e *CustomError) Error() string {
return fmt.Sprintf("[%d] %s", e.code, e.message)
}
func main() {
var err error = &CustomError{
message: "Something went wrong",
code: 500,
}
fmt.Println(err)
}
Best Practices
✅ Good: Small Interfaces
// DO: Define small, focused interfaces
type Reader interface {
Read() ([]byte, error)
}
type Writer interface {
Write([]byte) (int, error)
}
❌ Bad: Large Interfaces
// DON'T: Create large interfaces with many methods
type Everything interface {
Read() ([]byte, error)
Write([]byte) (int, error)
Close() error
Seek(int64, int) (int64, error)
// ... many more methods
}
✅ Good: Accept Interfaces
// DO: Accept interfaces, return concrete types
func Process(r Reader) ([]byte, error) {
return r.Read()
}
✅ Good: Use Type Assertion Safely
// DO: Check type assertion result
if str, ok := v.(string); ok {
fmt.Println(str)
} else {
fmt.Println("Not a string")
}
Summary
Go interfaces provide:
- Implicit implementation without explicit declaration
- Polymorphism through duck typing
- Composition through interface embedding
- Type safety with type assertions
- Flexibility for writing generic code
These features make interfaces powerful tools for abstraction in Go.
Comments