Interfaces in Go provides a way to specify the behavior of an object. Interfaces allow us to write more flexible and scalable code in Go. It is also a mechanism to achieve polymorphism in Go.

type Interface interface {
     Method1(int, string) (int, error)
     Method2(int) bool
     Method3(string)
}

Interfaces specify methods (or behavior) but do not provide an implementation for those methods. Types implementing the interface provide an implementation of methods.

Composition of Interfaces Link to heading

Interfaces can embed methods from another interface. It embeds all exported (name starting with capital case) and non-exported (name starts with small case) methods from other interfaces.

type Interface1 interface {
    Method1(int, string) (int, error)
    method2(int) bool
    method3(string)
}
type Interface2 interface {
    Method4(int, string) (int, error)
    Method5(string)
    Interface1         //Embedding Interface1 
}

Here, Interface2 will have Method1, method2 and method3 along with Method4 and Method5.

If an interface Y embed another interface Z, if Y is embedded by X then all methods of Z is also embedded into X.

type I1 interface {
   Method1(int, string) (int, error)
   I2
}

type I2 interface {
   Method2(int, string) (int, error)
   I3
}
type I3 interface {
    Method3(int, string) (int, error)
}

In the above example, interface I1 will have three methods Method1, Method2 and Method3.

Circular embedding of interfaces gets detected by the Go compiler and an error will be raised. Interface methods should also have unique names otherwise Go compiler will raise an error.

Implementing Interfaces Link to heading

Types implementing those interfaces need to provide an implementation for all methods (or behaviors) in the interface and should define methods with the desired names and signatures (Input and Output parameters).

In Go, interfaces are implemented implicitly. We don’t need to specify that some types implement an interface. Go compiler automatically does that if the type defines all methods of an interface.

type I interface {
   M1(string) string
   M2(string)
}

type T struct {}

func (T) M1(str string) string {
    return str
}

func (T) M2(str string) {
    fmt.Println(str)
}

A type can implement multiple interfaces.

type Interface1 interface {
    Method1(string) string
    Method2(string)
}
type Interface2 interface {
    Method3(string) string
}
type T struct {}

func (T) Method1(str string) string {
    return str
}

func (T) Method2(str string) {
    fmt.Println(str)
}
func (T) Method3(str string) string {
    
    return str
}

An interface can be implemented by multiple types.

type Interface interface {
    Method1(string) string
}

type T1 struct{}
type T2 struct{}

func (T1) Method1(str string) string {
    return str
}
func (T2) Method1(str string) string {
    return str
}

If a Type implements an interface then that Type value can be used wherever we need to use interface value. What it means is, we can pass a value of Type implementing an interface if a function accepts a parameter of the type interface.

type Interface interface {
    Method(string) string
}

type T struct{}

func (T) Method(str string) string {
    return str
}

func f1(i Interface) {
    fmt.Println(i.Method("Hello"))
}
func main() {
    f1(T{})
}

An interface type can hold a value of any Type implementing the interface. When we call a method on the interface value then Go dynamically determines which Type method should be invoked based on the type of value it is holding.

type Interface interface {
    Method(str string)
}
type T1 struct{}
type T2 struct{}

func (T1) Method(str string) {
    fmt.Println(str + " from T1 Method")
}
func (T2) Method(str string) {
    fmt.Println(str + " from T2 Method")
}
func main() {
    var i Interface
    i = T1{}
    i.Method("Hello") //invoke Method of T1
    i = T2{}
    i.Method("Hello") //invoke method of T2
}

When we invoke Method on i it checks the type of value it’s holding and calls the corresponding implementation of the Method. Above code gives below output when we run it.

Hello from T1 Method
Hello from T2 Method

An interface type value allows accessing only methods of interfaces. It hides methods of the exact type which value it holds.

type Interface interface {
    Method(str string)
}

type T1 struct{}

func (T1) Method(str string) {
   fmt.Println(str + " from T1 Method")
}
func (T1) Method1(str string) {
   fmt.Println(str + " from Method1 of T1")
}
func main() {
    var i Interface
    i = T1{}
    i.Method1("Hello") //throws an error
}

when we will try to run above code Go compiler will throw an error saying

i.Method1 undefined (type Interface has no field or method Method1)

Empty Interface Link to heading

We can also have an interface which does not have any methods. We call empty interface. Empty interfaces are satisfiable by any type.

type Interface interface {} //An empty interface

type T struct{}

func (T) Method(str string) {
    fmt.Println(" " + " from Method")
}
func main() {
    var i Interface = T{}
    _ = i
}

Thanks for the read. If you find something wrong or would like to suggest something please leave a comment below.