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.
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.
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.
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.
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.
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.
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.
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
This tutorial covered the interface keyword in Go with practical
examples of polymorphism, type assertions, and interface composition.
Author
List all Golang tutorials.