ZetCode

Golang interface keyword

last modified May 7, 2025

This tutorial explains how to use the interface keyword in Go. We'll cover interface basics with practical examples of polymorphism.

The interface type defines a set of method signatures. Any type that implements these methods implicitly satisfies the interface.

In Go, interfaces enable polymorphism and flexible code design. They allow different types to be treated uniformly if they implement the same interface.

Basic interface definition

A simple interface defines method signatures without implementations. This example shows a basic interface and its implementation.

basic_interface.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 main() {
    animals := []Speaker{Dog{}, Cat{}}
    
    for _, animal := range animals {
        fmt.Println(animal.Speak())
    }
}

The Speaker interface requires a Speak method. Both Dog and Cat implement it, allowing polymorphic usage.

Empty interface

The empty interface interface{} has no methods. All types implement it, making it useful for generic functions.

empty_interface.go
package main

import "fmt"

func describe(i interface{}) {
    fmt.Printf("Type: %T, Value: %v\n", i, i)
}

func main() {
    describe(42)
    describe("hello")
    describe(3.14)
    describe([]int{1, 2, 3})
}

The describe function accepts any type via the empty interface. It prints the type and value of whatever is passed to it.

Interface composition

Interfaces can embed other interfaces to create more complex contracts. This example combines multiple interfaces.

composite_interface.go
package main

import "fmt"

type Walker interface {
    Walk()
}

type Runner interface {
    Run()
}

type Athlete interface {
    Walker
    Runner
}

type Human struct{}

func (h Human) Walk() {
    fmt.Println("Human walking")
}

func (h Human) Run() {
    fmt.Println("Human running")
}

func main() {
    var athlete Athlete = Human{}
    athlete.Walk()
    athlete.Run()
}

Athlete combines Walker and Runner. Human implements both methods, satisfying the composite interface.

Type assertions

Type assertions check if an interface value holds a specific type. This example demonstrates safe type checking.

type_assertion.go
package main

import "fmt"

func checkType(i interface{}) {
    if s, ok := i.(string); ok {
        fmt.Printf("It's a string: %s\n", s)
    } else if n, ok := i.(int); ok {
        fmt.Printf("It's an int: %d\n", n)
    } else {
        fmt.Printf("Unknown type: %T\n", i)
    }
}

func main() {
    checkType("hello")
    checkType(42)
    checkType(3.14)
}

The checkType function uses type assertions to determine the underlying type of the interface value and handle each case appropriately.

Type switches

Type switches simplify type assertions with multiple cases. This example shows a cleaner way to handle different types.

type_switch.go
package main

import "fmt"

func process(i interface{}) {
    switch v := i.(type) {
    case int:
        fmt.Printf("Integer: %d\n", v)
    case string:
        fmt.Printf("String: %s\n", v)
    case bool:
        fmt.Printf("Boolean: %v\n", v)
    default:
        fmt.Printf("Unknown type: %T\n", v)
    }
}

func main() {
    process(42)
    process("gopher")
    process(true)
    process(3.14)
}

The type switch syntax i.(type) checks multiple possible types in a clean, readable way. Each case handles a different type.

Practical example: Database abstraction

Interfaces are powerful for creating abstractions. This example shows a simple database interface with multiple implementations.

database_interface.go
package main

import "fmt"

type Database interface {
    Connect() string
    Query(q string) string
}

type MySQL struct{}

func (m MySQL) Connect() string {
    return "MySQL connected"
}

func (m MySQL) Query(q string) string {
    return fmt.Sprintf("MySQL query: %s", q)
}

type PostgreSQL struct{}

func (p PostgreSQL) Connect() string {
    return "PostgreSQL connected"
}

func (p PostgreSQL) Query(q string) string {
    return fmt.Sprintf("PostgreSQL query: %s", q)
}

func main() {
    databases := []Database{MySQL{}, PostgreSQL{}}
    
    for _, db := range databases {
        fmt.Println(db.Connect())
        fmt.Println(db.Query("SELECT * FROM users"))
    }
}

The Database interface defines common operations. Both database types implement it, allowing uniform usage despite different implementations.

Interface satisfaction verification

Go can verify at compile time if a type satisfies an interface. This example shows explicit interface satisfaction checking.

interface_check.go
package main

import "fmt"

type Writer interface {
    Write([]byte) (int, error)
}

type ConsoleWriter struct{}

func (cw ConsoleWriter) Write(data []byte) (int, error) {
    n, err := fmt.Println(string(data))
    return n, err
}

func main() {
    var w Writer = ConsoleWriter{}
    w.Write([]byte("Hello, interfaces!"))
    
    // Compile-time check
    var _ Writer = (*ConsoleWriter)(nil)
}

The line var _ Writer = (*ConsoleWriter)(nil) verifies that ConsoleWriter satisfies Writer at compile time. This is a common Go idiom.

Source

Go language specification

This tutorial covered the interface keyword in Go with practical examples of polymorphism, type assertions, and interface composition.

Author

My name is Jan Bodnar, and I am a passionate programmer with extensive programming experience. I have been writing programming articles since 2007. To date, I have authored over 1,400 articles and 8 e-books. I possess more than ten years of experience in teaching programming.

List all Golang tutorials.