Interface in Go

Intro

We declare an Interface much like as we define a user defined type. Interfaces decouple different types from each other so we can create more maintainable programs.

More:

  • An Interface is a Protocol, a Contract.

  • Bigger the Interface the weaker the abstraction. –> Rob Pike

  • It’s an abstract type. It doesn’t have any implementation. It only describes the expected behavior.

  • The opposite of Abstract Type is Concrete Type.

  • All the types in Go except Interface are of Concrete Type.

  • For e.g. Following are Concrete Types:

  // Concrete Types
  int
  string
  float64
  array
  struct
  slice
  map
  chan
  func
  • The Interface only defines the expected behavior.

  • Go does not have an implements keyword.

  • A Type satisfies an Interface automatically when it has all the methods of the Interface without explicitely specifying it.

  • Interface values are comparable.

  • Go interfaces are implicit. The implementing types don’t need to specify that they implement an interface.

Interface declaration example

  type MyInterface interface {
      foo() int
      bar() float64
      baz() string
  }

Interface Example

Interface is a reference type which contains some method definitions. Any type which implements all the methods defined by a reference type will satisfy this interface type automatically. Through interface, you can approach object-oriented programming. Check the following example:

package main

import "fmt"

type Foo interface {
  foo()
}

type A struct {
}

func (a A) foo() {
  fmt.Println("A foo")
}

func (a A) bar() {
  fmt.Println("A bar")
}

func callFoo(f Foo) {
  f.foo()
}

func main() {
  var a A
  callFoo(a)
}

The running result is:

A foo

Let’s analyze the code detailedly:

(1)

type Foo interface {
  foo()
}

The above code defines a interface Foo which has only one method: foo().

(2)

type A struct {
}

func (a A) foo() {
  fmt.Println("A foo")
}

func (a A) bar() {
  fmt.Println("A bar")
}

struct A has 2 methods: foo() and bar(). Since it already implements foo() method, it satisfies Foo interface.

(3)

func callFoo(f Foo) {
  f.foo()
}

func main() {
  var a A
  callFoo(a)
}

callFoo requires a variable whose type is Foo interface, and passing A is OK. The callFoo will use A’s foo() method, and “A foo” is printed.

Implementing interface with pointer receiver

Let’s change the main() function:

func main() {
  var a A
  callFoo(&a)
}

This time, the argument of callFoo() is &a, whose type is *A. Compile and run the program, you may find it also outputs: “A foo”. So *A type has all the methods which A has. But the reverse is not true:

package main

import "fmt"

type Foo interface {
  foo()
}

type A struct {
}

func (a *A) foo() {
  fmt.Println("A foo")
}

func (a *A) bar() {
  fmt.Println("A bar")
}

func callFoo(f Foo) {
  f.foo()
}

func main() {
  var a A
  callFoo(a)
}

Compile the program:

example.go:26: cannot use a (type A) as type Foo in argument to callFoo:
A does not implement Foo (foo method has pointer receiver)

You can see also *A type has implemented foo() and bar() methods, it doesn’t mean A type has both methods by default.

Empty Interface

Every type in Go implements the empty interface: interface{}. It doesn’t have any methods.

Eg.

  type someInterface interface {
  
  }

Note:

  • Do not use Empty Interface unless really necessary.

  • It can represent any Type of Value.

  • We can’t directly use the dynamic value of an empty interface value.

Using the empty interface value

  • To use a value from Empty Interface, we first need to extract it using Type Assertion.

  • Empty Interface Slice contains the Empty Interface Values.

  • Example use cases of empty interfaces:

    • A function that returns a value of interface{} can return any type.
    • We can store heterogeneous values in an array, slice, or map using the empty interface{} type.

Interface as nil

The interface type is actually a tuple which contains 2 elements: <type, value>:

  • A dynamic Value - value points to the actual value
  • A dynamic Type - type identifies the type of the variable which stores in the interface

The default value of an interface type is nil, which means both type and value are nil: <nil, nil>. When you check whether an interface is empty or not:

var err error
if err != nil {
  ...
}  

You must remember only if both type and value are nil means the interface value is nil.


Reference:
The Go Programming Language. https://github.com/NanXiao/golang-101-hacks


See also