Welcome To Golang By Example

Interface in Go (Golang)

This is the  chapter 21 of the golang comprehensive tutorial series. Refer to this link for other chapters of the series – Golang Comprehensive Tutorial Series

Next Tutorial – Iota
Previous Tutorial – Method

Now let’s check out the current tutorial. Below is the table of contents for current tutorial.

Overview

Interface is a type in Go which is a collection of method signatures. These collections of method signatures are meant to represent certain behaviour. The interface declares only the method set and any type which implements all methods of the interface is of that interface type.

Interface lets you use duck typing in golang. Now, what is duck typing?

Duck typing is a way in computer programming which lets you do duck test where we do not check type instead we check the only presence of some attributes or methods. So what really matters is whether an object has certain attributes and methods and not its type.

Duck typing comes from the below phrase

If it walks like a duck and quack like a duck then it must be duck

Coming back to interface again. So what is interface?  As mentioned before also it is a collection of method signatures. It defines the exact set of methods that a type might have. Below is the signature of an interface, it has only method signatures

type name_of_interface interface{
//Method signature 1
//Method signature 2
}

Let’s understand the concept with the help of an example. Things will be more clear then. Let’s define an interface named animal. The animal interface has two methods breathe and walk. It defines only the method signatures and nothing else.

type animal interface {
    breathe()
    walk()
}

A method signature would include

With the above declaration, we created a new interface type i.e animal. It is ok to define a variable of animal type.

Let’s create a variable of animal interface type.

package main

import "fmt"

type animal interface {
    breathe()
    walk()
}

func main() {
    var a animal
    fmt.Println(a)
}

Output

nil

As seen in the above program it is ok to create a variable of interface type. It prints nil as the default zero value of an interface is nil.

Implementing an Interface

Any type which implements the breathe and walk method then it is said to implement the animal interface. So if we define a lion struct and implements the breathe and walk method then it will implement the animal interface.

package main

import "fmt"

type animal interface {
    breathe()
    walk()
}

type lion struct {
    age int
}

func (l lion) breathe() {
    fmt.Println("Lion breathes")
}

func (l lion) walk() {
    fmt.Println("Lion walk")
}

func main() {
    var a animal
    a = lion{age: 10}
    a.breathe()
    a.walk()
}

Output

Lion breathes
Lion walk

We declare a variable of animal interface type

var a animal

Then we assign an instance of lion struct to it.

a = lion{}

Assigning an instance of lion struct to a variable of animal interface type works because lion struct implements both breathe and walk method of the animal. The type is not checked during this assignment, instead, it is enough to check that the type assigned does implement breathe and walk method.  The concept is similar to duck typing,  a lion can breathe and walk like an animal and hence it is an animal.

If you notice that there is no explicit declaration that the lion type implements the animal interface. This brings a very important property related to interface – ‘Interface are implemented implicitly

Interface are implemented implicitly

There is no explicit declaration that a type implements an interface. In fact, in Go there doesn’t exist any “implements” keyword similar to Java.  A type implements an interface if it implements all the methods of the interface.

As seen above, It is correct to define a variable of an interface type and we can assign any concrete type value to this variable if the concrete type implements all the methods of the interface.

There is no explicit declaration that says that lion struct implements the animal interface. During compilation, go notices that lion struct implements all methods of animal interface hence it is allowed. Any other type which implements all methods of the animal interface becomes of that interface type.

Let’s see a more complex example of another type implementing the animal interface.

If we define a dog struct and it implements the breathe and walk method then it will also be an animal.

package main

import "fmt"

type animal interface {
    breathe()
    walk()
}

type lion struct {
     age int
}

func (l lion) breathe() {
    fmt.Println("Lion breathes")
}

func (l lion) walk() {
    fmt.Println("Lion walk")
}

type dog struct {
     age int
}

func (l dog) breathe() {
    fmt.Println("Dog breathes")
}

func (l dog) walk() {
    fmt.Println("Dog walk")
}

func main() {
    var a animal
    
    a = lion{age: 10}
    a.breathe()
    a.walk()
  
    a = dog{age: 5}
    a.breathe()
    a.walk()
}

Output

Lion breathes
Lion walk
Dog breathes
Dog walk

Both lion and dog implement the breathe and walk method hence they are of animal type and can correctly be assigned to a variable of interface type.

The animal interface variable a was assigned a lion instance first and then the same variable was assigned a dog instance. So the type which interface variable refers to is dynamic.  It dynamically holds a reference to the underlying type.

Two important points to note:

cannot use lion literal (type lion) as type animal in assignment:

It is also to be noted that the methods defined by the type,  should match the entire signature of methods in the interface ie., it should match

Imagine that animal interface has another method speed which returns the int value of speed of the animal

type animal interface {
    breathe()
    walk()
    speed() int
}

If the lion struct has speed method as below which does not return the int value, then the lion struct will not implement animal interface

func (l lion) speed()

Below compilation error will be raised on assigning the lion instance to a variable of type animal

cannot use lion literal (type lion) as type animal in assignment:
        lion does not implement animal (wrong type for speed method)
                have speed()
                want speed() int

So in essence, method signatures are important while implementing an interface

Interface types as argument to a function

A function can accept an argument of an interface type. Any type which implements that interface can be passed as that argument to that function. For example, in the below code, we have callBreathe and callWalk function which accept an argument of animal interface type. Both lion and dog instance can be passed to this function. We create an instance of both lion and dog type and pass it to the function.

It works similarly to the assignment we discussed above. During compilation no type is checked while calling the function,  instead, it is enough to check that the type passed to the function does implement breathe and walk method.

package main

import "fmt"

type animal interface {
	breathe()
	walk()
}

type lion struct {
     age int
}

func (l lion) breathe() {
	fmt.Println("Lion breathes")
}

func (l lion) walk() {
	fmt.Println("Lion walk")
}

type dog struct {
     age int
}

func (l dog) breathe() {
	fmt.Println("Dog breathes")
}

func (l dog) walk() {
	fmt.Println("Dog walk")
}

func main() {
	l := lion{age: 10}
	callBreathe(l)
	callWalk(l)

	d := dog{age: 5}
	callBreathe(d)
	callWalk(d)
}

func callBreathe(a animal) {
	a.breathe()
}

func callWalk(a animal) {
	a.breathe()
}

Output

Lion breathes
Lion walk
Dog breathes
Dog walk

In the above code, we have callBreathe and callWalk function which accept an argument of animal interface type. Both lion and dog instance can be passed to this function. We create an instance of both lion and dog type and pass it to the function. During compilation no type is checked while calling the function, instead, it is enough to check that the type passed to the function does implement breathe and walk method.

Why Interface

Below are some benefits of using interface.

For eg imagine an application interacting with a database layer. If the application interacts with the database using the interface, then it never gets to know about what kind of database is being used in the background. You can change the type of database in the background, let’s say from arango db to mongo db without any change in the application layer as it interacts with the database layer via an interface which both arango db and mongo db implement.

Different countries have different ways of calculating the tax. This can be represented by means of an interface.

type taxCalculator interface{
    calculateTax()
}

Now different countries can have their own struct and can implement the calculateTax() method. The same calculateTax method is used in different contexts to calculate tax. When the compiler sees this call it delays which exact method to be called at run time.

package main

import "fmt"

type taxSystem interface {
    calculateTax() int
}
type indianTax struct {
    taxPercentage int
    income        int
}
func (i *indianTax) calculateTax() int {
    tax := i.income * i.taxPercentage / 100
    return tax
}
type singaporeTax struct {
    taxPercentage int
    income        int
}
func (i *singaporeTax) calculateTax() int {
    tax := i.income * i.taxPercentage / 100
    return tax
}
type usaTax struct {
    taxPercentage int
    income        int
}
func (i *usaTax) calculateTax() int {
    tax := i.income * i.taxPercentage / 100
    return tax
}
func main() {
    indianTax := &indianTax{
        taxPercentage: 30,
        income:        1000,
    }
    singaporeTax := &singaporeTax{
        taxPercentage: 10,
        income:        2000,
    }


    taxSystems := []taxSystem{indianTax, singaporeTax}
    totalTax := calculateTotalTax(taxSystems)


    fmt.Printf("Total Tax is %d\n", totalTax)
}
func calculateTotalTax(taxSystems []taxSystem) int {
    totalTax := 0
    for _, t := range taxSystems {
        totalTax += t.calculateTax() //This is where runtime polymorphism happens
    }
    return totalTax
}

Output:

Total Tax is 300

Now below is the line where run time polymorphism happens. 

 totalTax += t.calculateTax() //This is where runtime polymorphism happens

The correct calculateTax() method is called based upon weather the instance is of type singaporeTax struct tax or indianTax struct

Pointer Receiver  while implementing an interface

A method of a type can either have a pointer receiver or a value receiver. In the above examples, we only worked with the value receiver. It is to be noted that the pointer receiver can also be used to implement an interface. But there is a caveat here

Example to demonstrate the first point above

package main

import "fmt"

type animal interface {
    breathe()
    walk()
}

type lion struct {
    age int
}

func (l lion) breathe() {
    fmt.Println("Lion breathes")
}

func (l lion) walk() {
    fmt.Println("Lion walk")
}

func main() {
    var a animal

    a = lion{age: 10}
    a.breathe()
    a.walk()

    a = &lion{age: 5}
    a.breathe()
    a.walk()
}

Output

Lion breathes
Lion walk
Lion breathes
Lion walk

The lion structs implements the animal interface using value receiver. Hence it works for both variable of type lion as well as pointer to variable of type lion.

This works

a = lion{age: 10}

as well as this

a = &lion{age: 5}

Example to demonstrate the second point above. The lion structs implements the animal interface using pointer receiver. Hence it  works for only for pointer to variable of type lion.

So this works

 a = &lion{age: 5}

but this raises compilation error

a = lion{age: 10}
cannot use lion literal (type lion) as type animal in assignment:
        lion does not implement animal (breathe method has pointer receiver)

See full working code

package main

import "fmt"

type animal interface {
	breathe()
	walk()
}

type lion struct {
	age int
}

func (l *lion) breathe() {
	fmt.Println("Lion breathes")
}

func (l *lion) walk() {
	fmt.Println("Lion walk")
}

func main() {
	var a animal

	//a = lion{age: 10}
	a.breathe()
	a.walk()

	a = &lion{age: 5}
	a.breathe()
	a.walk()
}

Uncomment the line

a = lion{age: 10}

and it will raise compilation error

cannot use lion literal (type lion) as type animal in assignment:
        lion does not implement animal (breathe method has pointer receiver)

Non-struct Custom Type Implementing an interface

So far we have only seen examples of struct type implementing an interface.  It is also perfectly ok for any non-struct custom type to implement an interface. Let’s see an example

package main

import "fmt"

type animal interface {
	breathe()
	walk()
}

type cat string

func (c cat) breathe() {
	fmt.Println("Cat breathes")
}

func (c cat) walk() {
	fmt.Println("Cat walk")
}

func main() {
	var a animal

	a = cat("smokey")
	a.breathe()
	a.walk()
}

Output

Cat breathes
Cat walk

The above program illustrates the concept that any custom type can also implement an interface. The cat is of string type and it implements the breathe and walk method hence it is correct to assign an instance of cat type to a variable of animal type.

Type Implementing multiple interfaces

A type implements an interface if it defines all methods of an interface. If that defines all methods of another interface then it implements that interface. In essence, a type can implement multiple interfaces.

In below program, we have a mammal interface that has a feed method. The lion struct defines this method too hence it implements the mammal interface.

package main

import "fmt"

type animal interface {
    breathe()
    walk()
}

type mammal interface {
    feed()
}

type lion struct {
     age int
}
func (l lion) breathe() {
    fmt.Println("Lion breathes")
}
func (l lion) walk() {
    fmt.Println("Lion walk")
}
func (l lion) feed() {
    fmt.Println("Lion feeds young")
}
func main() {
    var a animal
    l := lion{}
    a = l
    a.breathe()
    a.walk()
    var m mammal
    m = l
    m.feed()
}

Output

Lion breathes
Lion walk
Lion feeds young

Zero Value of Interface

Default or zero value of an interface is nil. Below program demonstrates that

package main

import "fmt"
type animal interface {
    breathe()
    walk()
}

func main() {
    var a animal
    fmt.Println(a)
}

Output

nil

Inner Working of Interface

Like any other variable, an interface variable is represented by a type and value. Interface value, in turn under the hood, consists of two tuple

See below diagram which illustrates what we mentioned above

For eg in case of lion struct implementing the animal interface would be like below

Golang provides format identifiers to print the underlying type and underlying value represented by the interface value.

package main

import "fmt"

type animal interface {
    breathe()
    walk()
}

type lion struct {
    age int
}

func (l lion) breathe() {
    fmt.Println("Lion breathes")
}

func (l lion) walk() {
    fmt.Println("Lion walk")
}

func main() {
    var a animal
    a = lion{age: 10}
    fmt.Printf("Underlying Type: %T\n", a)
    fmt.Printf("Underlying Value: %v\n", a)
}

Output

Concrete Type: main.lion
Concrete Value: {10}

An interface can be embedded in other interface as well as it can be embedded in a struct. Let’s look at each one by one

Embedding Interfaces

An interface can be embedded in other interface as well as it can be embedded in a struct. Let’s look at each one by one

Embedding interface in other interface

An interface can embed any number of interfaces in it as well as it can be embedded in any interface. All the methods of the embedded interface become part of the embedding interface. It is a way of creating a new interface by merging some small interfaces. Let’s understand it with an example

Assume we have an interface animal as below

type animal interface {
    breathe()
    walk()
}

Let’s say there is another interface named human which embeds the animal interface

type human interface {
    animal
    speak()
}

So if any type needs to implement the human interface, then it has to define

package main

import "fmt"

type animal interface {
	breathe()
	walk()
}

type human interface {
	animal
	speak()
}

type employee struct {
	name string
}

func (e employee) breathe() {
	fmt.Println("Employee breathes")
}

func (e employee) walk() {
	fmt.Println("Employee walk")
}

func (e employee) speak() {
	fmt.Println("Employee speaks")
}

func main() {
	var h human

	h = employee{name: "John"}
	h.breathe()
	h.walk()
	h.speak()
}

Output

Employee breathes
Employee walk
Employee speaks

As an other example, the ReaderWriter interface of the io package of golang (https://golang.org/pkg/io/#ReadWriter) embeds two other interfaces

type ReadWriter interface {
    Reader
    Writer
}

Embedding interface in a struct

An interface can be embedded in a struct as well.  All the methods of the embedded interface can be called via that struct. How these methods will be called will depend upon whether the embedded interface is a named field or an unnamed/anonymous field. 

Let’s see a program illustrating above points

package main

import "fmt"

type animal interface {
    breathe()
    walk()
}

type dog struct {
    age int
}

func (d dog) breathe() {
    fmt.Println("Dog breathes")
}

func (d dog) walk() {
    fmt.Println("Dog walk")
}

type pet1 struct {
    a    animal
    name string
}

type pet2 struct {
    animal
    name string
}

func main() {
    d := dog{age: 5}
    p1 := pet1{name: "Milo", a: d}

    fmt.Println(p1.name)
    // p1.breathe()
    // p1.walk()
    p1.a.breathe()
    p1.a.walk()

    p2 := pet2{name: "Oscar", animal: d}
    fmt.Println(p1.name)
    p2.breathe()
    p2.walk()
    p1.a.breathe()
    p1.a.walk()
}

Output

Milo
Dog breathes
Dod walk

Oscar
Dog breathes
Dog walk
Dog breathes
Dog walk

We declared two struct pet1 and pet2pet1 struct has named animal interface in it

type pet1 struct {
    a    animal
    name string
}

pet2 has unnamed/anonymous animal interface embedded

type pet2 struct {
    animal
    name string
}

For an instance of pet1 struct we call the breathe() and walk() method like this

p1.a.breathe()
p1.a.walk()

Directly calling these methods with raise compilation error

p1.breathe()
p1.walk()
p1.breathe undefined (type pet1 has no field or method breathe)
p1.walk undefined (type pet1 has no field or method walk)

For an instance of pet2 struct we can call the breathe() and walk() method like directly

p2.breathe()
p2.walk()

We can directly access the methods of embedded interface if the embedded interface is anonymous or unnamed.

Below is also valid and another way fo called methods of unnamed/anonymous embedded interface

p2.animal.breathe()
p2.animal.walk()

Also note that while creating the instance of either the pet1 or pet2 struct, the embedded interface i.e animal is initialised with a type implementing  it i.e dog .

p1 := pet1{name: "Milo", a: d}
p2 := pet2{name: "Oscar", animal: d}

If we don’t initialise the embedded interface animal, then it will be intialised with the zero value of the interface which is nil. Calling breathe() and walk() method  on such an instance of pet1 or pet2 struct will create a panic.

Access Underlying Variable of Interface

The underlying variable can be accessed in two ways

Type Assertion

Type assertion provides a way to access the underlying variable inside the interface value of the interface by asserting the correct type of underlying value. Below is the syntax for that where i is an interface.

val := i.({type})

The above statement is asserting that the type of underlying value in the interface is of type {type}. If this is true then the underlying value is assigned to val. If not then the above statement panics.

package main

import "fmt"

type animal interface {
	breathe()
	walk()
}

type lion struct {
	age int
}

func (l lion) breathe() {
	fmt.Println("Lion breathes")
}

func (l lion) walk() {
	fmt.Println("Lion walk")
}

type dog struct {
	age int
}

func (d dog) breathe() {
	fmt.Println("Dog breathes")
}

func (d dog) walk() {
	fmt.Println("Dog walk")
}

func main() {
	var a animal

	a = lion{age: 10}
	print(a)

}

func print(a animal) {
	l := a.(lion)
	fmt.Printf("Age: %d\n", l.age)

	//d := a.(dog)
	//fmt.Printf("Age: %d\n", d.age)
}

Output

Age: 10

This is how we assert the variable a of type animal to have underlying type as lion.

l := a.(lion)

Below line will create a panic as underlying type is lion and not dog. Uncomment the line to check it out

//d := a.(dog)

Type assertion provides another way to get the underlying value and which also prevents a panic. The syntax for that is

val, ok := i.()

In this case type assertion returns two values, the first value is the same as discussed above, the other value is boolean indicating whether the type assertion was correct or not. This value is

So the second is a good way of doing type assertion since it prevents a panic. Let’s see an example

package main

import "fmt"

type animal interface {
	breathe()
	walk()
}

type lion struct {
	age int
}

func (l lion) breathe() {
	fmt.Println("Lion breathes")
}

func (l lion) walk() {
	fmt.Println("Lion walk")
}

type dog struct {
	age int
}

func (d dog) breathe() {
	fmt.Println("Dog breathes")
}

func (d dog) walk() {
	fmt.Println("Dog walk")
}

func main() {
	var a animal

	a = lion{age: 10}
	print(a)

}

func print(a animal) {
	l, ok := a.(lion)
	if ok {
		fmt.Println(l)
	} else {
		fmt.Println("a is not of type lion")
	}

	d, ok := a.(dog)
	if ok {
		fmt.Println(d)
	} else {
		fmt.Println("a is not of type lion")
	}
}

Output:

{10}
a is not of type lion

Let’s move on to type switch now.

Type Switch

Type switch enables us to do above type assertion in series. See below code example for the same

package main

import "fmt"

type animal interface {
	breathe()
	walk()
}

type lion struct {
	age int
}

func (l lion) breathe() {
	fmt.Println("Lion breathes")
}

func (l lion) walk() {
	fmt.Println("Lion walk")
}

type dog struct {
	age int
}

func (d dog) breathe() {
	fmt.Println("Dog breathes")
}

func (d dog) walk() {
	fmt.Println("Dog walk")
}

func main() {
	var a animal

	x = lion{age: 10}
	print(x)

}

func print(a animal) {
	switch v := a.(type) {
	case lion:
		fmt.Println("Type: lion")
	case dog:
		fmt.Println("Type: dog")
	default:
		fmt.Printf("Unknown Type %T", v)
	}
}

Output:

Type: lion

In the code above, using the type switch we determine the type of value contained in interface variable x is lion or dog or some other type. It is also ok to add more different types in case statement

Empty interface

An empty interface has no methods , hence by default all concrete types implement the empty interface. If you write a function that accepts an empty interface then you can pass any type to that function. See working code below

package main

import "fmt"

func main() {
    test("thisisstring")
    test("10")
    test(true)
}

func test(a interface{}) {
    fmt.Printf("(%v, %T)\n", a, a)
}

Output

(thisisstring, string)
(10, string)
(true, bool)

Conclusion

This is all about interface in go. Hope you have liked this article. Please share feedback/improvemets/mistakes in comments.

Next Tutorial – Iota
Previous Tutorial – Method