[Golang] Interface

2021-11-17 hit count image

Let's see what the Interface is and how to use it in Golang.

Outline

In this blog post, I will introduce what the Interface is and how to use the interface in Golang. You can see the full source code of this blog post on the link below.

Interface

In Golang, the interface is an abstract interaction, not a concrete object, and is used to show relationships.

You can define the interface in Golang like the below.

type INTERFACE_NAME interface {
  METHOD_NAME(PARAMETER_NAME) RETURN_TYPE
  ...
}

In Golang, you can use the type keyword and add the interface keyword after the interface name to define the interface.

In the interface, you can define the methods that are not implemented (only method names, parameters, and return types), and we’ll recognize the types with these declared methods as the interface we defined.

In Golang, the interface is one of the types, and you can make variables with the interface.

To check this, create the main.go file and modify it like the below.

package main

import "fmt"

type SampleInterface interface {
  SampleMethod()
}

func main() {
  var s SampleInterface
  fmt.Println(s)
}

When you execute the code, you can see the following result.

# go run main.go
<nil>

The interface is used for abstraction that hides internal behavior and is mainly used for decoupling that eliminates dependence through abstraction.

Interface default value

In Golang, the interface default value is nil. To check this, modify the main.go file like the below.

package main

import "fmt"

type SampleInterface interface {
  SampleMethod()
}

func main() {
  var s SampleInterface
  fmt.Println(s)
  s.SampleMethod()
}

After writing the code, execute the command below to make a module.

go mod init github.com/dev-yakuza/study-golang/interface/nil
go mod tidy
go build

When you execute the command, you can see the file named nil same location as the main.go file.

When you execute the file, you can see the following result.

# ./nil
<nil>
panic: runtime error: invalid memory address or nil pointer dereference

There is no grammatical error in the example above. However, the interface value is not assigned so the runtime error(nil pointer error) occurs.

If you want to know more details about the Module in Golang, see the following link.

Interface rule

There are some rules for the interface in Golang.

  1. Methods should have the method name.
  2. Even if the parameters are different, the same name methods can’t exist.
  3. Methods in the interface doesn’t include the implements
type SampleInterface interface {
  String() string
  String(a int) string // Error: duplicated method name
  _(x int) int         // Error: no name method
}

To check this, modify the main.go file like the below.

package main

import "fmt"

type SampleInterface interface {
  String() string
  String(a int) string // Error: duplicated method name
  _(x int) int         // Error: no name method
}

func main() {
  var s SampleInterface
  fmt.Println(s)
}

When the code is executed, you can see the following result.

# go run main.go
# command-line-arguments
./main.go:7:2: duplicate method String
./main.go:8:2: methods must have a unique non-blank name

Example

Let’s make a simple example to understand the interface in Golang. First, create the main.go file and modify it like the below.

package main

import "fmt"

type Human interface {
  Walk() string
}

type Student struct {
  Name string
  Age  int
}

func (s Student) Walk() string {
  return fmt.Sprintf("%s can walk", s.Name)
}

func (s Student) GetAge() int {
  return s.Age
}

func main() {
  s := Student{Name: "John", Age: 20}
  var h Human

  h = s
  fmt.Println(h.Walk())
  // fmt.Println(h.GetAge()) // ERROR
}

When you execute the code, you can see the following result.

# go run main.go
John can walk

Let’s see the details of the code. There is the Human interface and the interface has the Walk function.

type Human interface {
  Walk() string
}

And the Student struct type has the Name and Age variables, and it has the Walk and GetAge methods.

type Student struct {
  Name string
  Age  int
}

func (s Student) Walk() string {
  return fmt.Sprintf("%s can walk", s.Name)
}

func (s Student) GetAge() int {
  return s.Age
}

Next, the variable is created with the Student struct, and is assigned to the Human interface.

func main() {
  s := Student{Name: "John", Age: 20}
  var h Human

  h = s
  fmt.Println(h.Walk())
  // fmt.Println(h.GetAge()) // ERROR
}

The variabe that is assgined to Human has Walk function, so the Walk function in the Human variable can be called, but the GetAge function can’t be called.

Dock typing

You can impelement Duck typing with the interface in Golang. Duck typing means that if you see a bird and it walks like a duck, flies like a duck, and makes a sound like a duck, the bird will be called a duck.

In other words, regardless of what variables and functions are in the object, you can call the object as your interface that you want to define.

Let’s see an example to understand it. First, make the user/main.go file and modify it like the below.

package user

import "fmt"

type User struct {
  Name string
}

func (u User) Walk() {
  fmt.Println("Walking")
}
func (u User) Talk() {
  fmt.Println("Talking")
}

And then, execute the command below to make a module.

# cd user
go mod init github.com/dev-yakuza/study-golang/interface/user
go mod tidy

This user package has the User type, and the User type has the Walk and Talk methods. Next, let’s do Duck typing with the user package via the interface.

To do Duck typing, create the main/main.go file in the same directory as the user folder and modify it like the below.

package main

import (
  "fmt"

  "github.com/dev-yakuza/study-golang/interface/user"
)

type Human interface {
  Walk()
  Talk()
}

func main() {
  u := user.User{Name: "John"}
  fmt.Println(u)

  h := Human(u)
  fmt.Println(h)
}

And then, execute the command below to make a module and connect the local module.

go mod init github.com/dev-yakuza/study-golang/interface/main
go mod edit -replace github.com/dev-yakuza/study-golang/interface/user=../user
go mod tidy

When you execute the module, you can see the following result.

# go run main.go
{John}
{John}

As you see the main/main.go, the User type is replaced with the Human interface. We don’t care about the variables and methods of the User type. Just, if the type has Walk and Talk methods, it can be called as Human interface in the main package.

Like this, when you use the external packages, the interface can change the object to suit your code and ust it. Of course, when you create the external package, you don’t need to think about the interface. You just implement the variable and methods on your side. So, sometimes, the type in the external package doesn’t implement the methods that you defined in the interface. At this time, the compile error occurs.

Duck typing enables coding centered on users who use the code. The external package providers provide the concrete object, and the programmers who use the external package define the interface and use the object by their own type.

Embedded interface

In Golang, the interface can be embedded in other interfaces. This is called the Embedded interface in Golang.

type Reader interface {
  Read() (n int, err error)
  Close() error
}

type Writer interface {
  Write() (n int, err error)
  Close() error
}

type ReadWriter interface {
  Reader
  Writer
}

The ReadWriter interface includes the Reader and Writer interface, and has the Read, Write and Close methods.

Convert type

In Golang, you can convert the interface to the concrete type like the below.

var a Interface
t := a.(ConcreteType)

To convert the interface to the type, you can use .(type). When you use the code like above, a ConcreteType variable is created and assigned to the t variable.

You can convert the interface to the same inteface type, but if variables or methods that the type doesn’t have are called, the runtime error occurs.

To check this, create the main.go file and modify it like the below.

package main

import "fmt"

type Human interface {
  Learn()
}

type Teacher struct {
  Name string
}

func (m *Teacher) Learn() {
  fmt.Println("Teacher can learn")
}

func (m *Teacher) Teach() {
  fmt.Println("Teacher can teach")
}

type Student struct {
  Name string
}

func (m *Student) Learn() {
  fmt.Println("Student can learn")
}

func Study(h Human) {
  if h != nil {
    h.Learn()

    var s *Student
    s = h.(*Student)
    fmt.Printf("Name: %v\n", s.Name)
    // s.Teach() // ERROR
  }
}

func main() {
  Study(&Student{Name: "John"})
}

When the code is executed, the following result is shown.

# go run main.go
Student can learn
Name: John

WHen you convert the intefacec, if the type is not the corresponding interface type, the compile error occurs. To check this, modify the main.go file like the below.

package main

import "fmt"

type Stringer interface {
  String() string
}

type Student struct {
}

func main() {
  var stringer Stringer
  s := stringer.(*Student)
  fmt.Println(s)
}

When you execute the code, you can see the following result.

# go run main.go
# command-line-arguments
./main.go:14:15: impossible type assertion:
        *Student does not implement Stringer (missing String method)

Convert different interface

In Golang, you can convert to different interface types. There is no problem with grammar at all, but when you actually run it and there is no corresponding method, the runtime error occurs.

To check this, create the main.go file and modify it like the below.

package main

import "fmt"

type Teacher interface {
  Teach()
}

type Student interface {
  Learn()
}

type Person struct {
}

func (f *Person) Learn() {
  fmt.Println("Person can learn")
}

func Teach(s Student) {
  t := s.(Teacher)
  t.Teach()
}

func main() {
  p := &Person{}
  Teach(p)
}

When you execute the code, you can see the following result.

# go run main.go
panic: interface conversion: *main.Person is not main.Teacher: missing method Teach

After receiving the variable of the Person type as the Student interface and converting the assigned Student interface to the Teacher interface, the Teach method of the Teacher interface is called. There is no grammar problem, so there is no compile error. However, when the code is actually executed, the runtime error occurs because there is no Teach method in the Person type.

To prevent this issue, Golang provides a feature to check whether the type conversion is successful.

How to check type conversion succeeded

Converting between the interfaces is not a grammer problem. So, there is no compile error when you build the code. However, there is a possibility of an runtime error. To prevent the runtime error, we can check the type conversion succeeded in Golang.

var a Interface
t, ok := a.(ConcreteType)
  • t: the result of the type conversion
  • ok: the result of the type conversion succeeded or not

To check this, modify the main.go file like the below.

package main

import "fmt"

type Teacher interface {
  Teach()
}

type Student interface {
  Learn()
}

type Person struct {
}

func (f *Person) Learn() {
  fmt.Println("Person can learn")
}

func main() {
  p := &Person{}
  s := Student(p)
  s.Learn()

  t, ok := s.(Teacher)
  fmt.Println(ok)
  if ok {
    t.Teach()
  }
}

When you execute the code, you can see the following result.

# go run main.go
Person can learn
false

The Person type can be converted to the Student interface, but the Student interface doesn’t have the Teach method, so it can’t be converted to the Teacher interface.

So, when you try to convert the interface, you can see the result is false.

Like this, you can check the type conversion succeeded or not. And when converting the interface, it is mainly used as follows.

if t, ok := s.(Teacher); ok {
  t.Teach()
}

Empty interface

Sometime, the empty interface is used in Golang. The empty interface doesn’t have any methods, so any types can be assigned to it.

interface {}

This empty interface is used in the following cases.

package main

import "fmt"

type Student struct {
  Age int
}

func Print(v interface{}) {
  switch v := v.(type) {
  case int:
    fmt.Println("v is int", v)
  case float64:
    fmt.Println("v is float64", v)
  case string:
    fmt.Println("v is string", v)
  default:
    fmt.Printf("Not supported %T:%v", v, v)
  }
}

func main() {
  Print(10)
  Print(3.14)
  Print("Hello word")
  Print(Student{Age: 10})
}

When the code is executed, the following result is shown.

# go run main.go
v is int 10
v is float64 3.14
v is string Hello word
Not supported main.Student:{10}%

Completed

Done! we’ve seen what the interface is and how to use the interface in Golang. Also, we’ve learned Duck typing and how to convert the inteface to the concrete type or the different interface.

Was my blog helpful? Please leave a comment at the bottom. it will be a great help to me!

Posts