DEV Community

Sheary Tan
Sheary Tan

Posted on

Continue - Empty Interfaces in Go

Empty interface

As the name stated, it does not contain any methods. This is useful when we want to store different types of data.

From the previous post, we only use numbers for our example. What if we want to include different types of data?

For example:

package main

import "fmt"

type animal struct {
    age   int
    color string
    sound string
}

type cat struct {
    animal
    bossy bool
}

type dog struct {
    animal
    friendly bool
}

func main() {
    cassy := cat{animal{3, "white", "meow"}, true}
    kitty := cat{animal{2, "black", "meow"}, true}
    missy := cat{animal{1, "brown", "meow"}, true}
    cats := []cat{cassy, kitty, missy}

    rocky := dog{animal{4, "brown", "woof"}, true}
    lucy := dog{animal{3, "white", "woof"}, true}
    tucker := dog{animal{2, "black", "woof"}, true}
    dogs := []dog{rocky, lucy, tucker}

    for i, val := range cats {
        fmt.Println(i, " - ", val)
    }

    for i, val := range dogs {
        fmt.Println(i, " - ", val)
    }
}

// 0  -  {{3 white meow} true}
// 1  -  {{2 black meow} true}
// 2  -  {{1 brown meow} true}
// 0  -  {{4 brown woof} true}
// 1  -  {{3 white woof} true}
// 2  -  {{2 black woof} true}
Enter fullscreen mode Exit fullscreen mode

It is a simple code so I don't think I would need to explain here...Well alright.. So we have two types here with dog() and cat(), bunch of dogs and cats, and two for loops to loop through the dogs and cats and finally print out the dogs and cats.

See the problem here? Same as the previous post, why do we need to write two different functions (in this case it's the for loops) to do the same thing?

But what's different from the previous post is that we have different types here: int, string, and bool in the type.

Instead why don't we include our friend interface:

package main

import "fmt"
Enter fullscreen mode Exit fullscreen mode
type animals interface{}
Enter fullscreen mode Exit fullscreen mode
type animal struct {
    age   int
    color string
    sound string
}

type cat struct {
    animal
    bossy bool
}

type dog struct {
    animal
    friendly bool
}
Enter fullscreen mode Exit fullscreen mode
func main() {
    cassy := cat{animal{3, "white", "meow"}, true}
    kitty := cat{animal{2, "black", "meow"}, true}
    missy := cat{animal{1, "brown", "meow"}, true}

    rocky := dog{animal{4, "brown", "woof"}, true}
    lucy := dog{animal{3, "white", "woof"}, true}
    tucker := dog{animal{2, "black", "woof"}, true}

    friends := []animals{cassy, kitty, missy, rocky, lucy, tucker}

    for i, val := range friends {
        fmt.Println(i, " - ", val)
    }
}


// 0  -  {{3 white meow} true}
// 1  -  {{2 black meow} true}
// 2  -  {{1 brown meow} true}
// 3  -  {{4 brown woof} true}
// 4  -  {{3 white woof} true}
// 5  -  {{2 black woof} true}
Enter fullscreen mode Exit fullscreen mode

So here we have got an empty animal interface, recall the statement of 'This is useful when we want to store different types of data.', we definitely have a bunch of different types for the type cat and dog.

It is difficult to specify which type do we need exactly, so why don't we just chuck everything into something(empty interface) that accept everything?

Or you can do this too:

Empty Interface as param

package main

import (
    "fmt"
)

type animal struct {
    age   int
    color string
    sound string
}

type cat struct {
    animal
    bossy bool
}

type dog struct {
    animal
    friendly bool
}
Enter fullscreen mode Exit fullscreen mode
func result(a interface{}) { // Here!
    fmt.Println(a)
}

func main() {
    cassy := cat{animal{3, "white", "meow"}, true}
    rocky := dog{animal{4, "brown", "woof"}, true}

    result(cassy)
    result(rocky)
}

// {{3 white meow} true}
// {{4 brown woof} true}
Enter fullscreen mode Exit fullscreen mode

Here I only included a dog and a cat just to reduce the complexity.

Or this:

Empty Interface as slice

package main

import "fmt"

type animal struct {
    age   int
    color string
    sound string
}

type cat struct {
    animal
    bossy bool
}

type dog struct {
    animal
    friendly bool
}
Enter fullscreen mode Exit fullscreen mode
func main() {
    cassy := cat{animal{3, "white", "meow"}, true}
    kitty := cat{animal{2, "black", "meow"}, true}
    missy := cat{animal{1, "brown", "meow"}, true}

    rocky := dog{animal{4, "brown", "woof"}, true}
    lucy := dog{animal{3, "white", "woof"}, true}
    tucker := dog{animal{2, "black", "woof"}, true}

        // Here!
    friends := []interface{}{cassy, missy, kitty, rocky, lucy, tucker}

    fmt.Println(friends)
}

// [{{3 white meow} true} {{1 brown meow} true} {{2 black meow} true} {{4 brown woof} true} {{3 white woof} true} {{2 black woof} true}]
Enter fullscreen mode Exit fullscreen mode

Which will return a slice (array).

The basic idea of interface: to prevent the repetition when calling methods. Empty interface? To prevent the repetition when calling methods with different types.

Thank you for reading my post, I am still a newbie Gopher but working hard to become a qualified Gopher :3

Top comments (0)