Skip to main content
โšก Calmops

Go Interfaces: Definition and Implementation

Go Interfaces: Definition and Implementation

Interfaces are central to Go’s design. They enable polymorphism and abstraction through implicit implementation.

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:

  1. Implicit implementation without explicit declaration
  2. Polymorphism through duck typing
  3. Composition through interface embedding
  4. Type safety with type assertions
  5. Flexibility for writing generic code

These features make interfaces powerful tools for abstraction in Go.

Comments