Coding With Fun
Home Docker Django Node.js Articles FAQ

The magic inerterface in Golang is not just an interface but a type


Jun 01, 2021 Article blog


Table of contents


This article is intended to introduce some other methods of interface The basics of interface can be seen in another article: Go Language Polymorphism and Interface Use

Universal type interface

Interfaces are a writing specification in Java and other languages, and in golang interface is actually a value that can be passed like a value. And at the bottom of it, it's actually a tuple of values and types.

Here's an example from golang official documentation:

package main


import (
 "fmt"
 "math"
)


type I interface {
 M()
}


type T struct {
 S string
}


func (t *T) M() {
 fmt.Println(t.S)
}


type F float64


func (f F) M() {
 fmt.Println(f)
}


func main() {
 var i I


 i = &T{"Hello"}
 describe(i)
 i.M()


 i = F(math.Pi)
 describe(i)
 i.M()
}


func describe(i I) {
 fmt.Printf("(%v, %T)\n", i, i)
}

In the code above, a method called describe is defined, in which we output two values, one for interface i and the other for interface i.

(Recommended course: Go tutorial)

The results we output are as follows:

 The magic inerterface in Golang is not just an interface but a type1

You can see that the interface stores information about both instances of the corresponding structure and the type of structure. So interface can be understood as a special type.

Indeed, we can understand interface as a universal data type that can receive any type of value. Let's look at the following usage:

var a1 interface{} = 1
var a2 interface{} = "abc"
list := make([]interface{}, 0)
list = append(list, a1)
list = append(list, a2)
fmt.Println(list)

In the code we create a slice of the interface{} which can receive any type of value and instance. I n addition, we can also receive the value of any structure interface{} the type interface. T here may be some confusion here, but it's easy to figure it out. interface represents a type that can receive the value of any type that implements the method specified in interface When we define inteface{} we actually define an empty interface which is equivalent to an empty interface that does not require any method, so any type can receive it, which is why it becomes a universal type.

Of course there is no problem with our reception, the question is how do we use the values of these interface types?

One way is that we can judge the type of variable for a interface T he method interface judgment is very simple, and we use .(type) method of judgment. I t returns a value and a bool type tag, just as the key value of the map is judged. We can use this tag to determine if this type is correct.

if v, ok := a1.(int); ok {
    fmt.Println(v)
}

Switch switch also possible if there are more types:

switch v := i.(type) {
case int:
    fmt.Println("int")
case string:
    fmt.Println("string")
}

Empty nil

interface empty value of the interface type is nil and None in Python means that a pointer points to the empty. I f we call a method on an empty pointer in Java or another language, it triggers NullPointerMethodError which is the empty pointer error. This is also the most common error we beginners encounter in programming, often because we forget to initialize the declaration.

But not in golang even nil can call interface method. For example:

type T struct {
 S string
}


func (t *T) M() {
 fmt.Println(t.S)
}


func main() {
 var i I
 var t *T
 i = t
 i.M()
}

We t to i and the problem is that t is not initialized, so it is a nil and then our i is also a nil W e call the M method for nil and in the M method we print the local variable S for t S Because t is a nil at the moment and it does not have this variable, it throws an error of invalid memory address or nil pointer derefernce which is an error in addressing an empty pointer.

To fix this error, it's simple, we can judge t in the M method, and if t find that t is a nil then we skip the logic of execution. When we change the M function to this, we don't trigger the problem of empty pointers.

func (t *T) M() {
    if t == nil {
        fmt.Println("nil")
        return
    }
 fmt.Println(t.S)
}

The problem of nil triggering exceptions is also one of the problems that beginners often encounter, which also requires us to remember to determine whether the object being called is nil when implementing the inner method of the structure, so as to avoid unnecessary problems.

(Recommended course: Go Web programming)

The type selection of the assignment

We all know that polymorphism is achieved through interface in golang and as long as the function defined in interface is implemented, then we can assign the corresponding instance to this interface type.

This may seem like a problem, but there's still a little bit of a problem with actual execution. Let's say we have this piece of code:

type Integer int


type Operation interface {
 Less(b Integer) bool
 Add(b Integer)
}




func (a Integer) Less(b Integer) bool {
 return a < b
}


func (a *Integer) Add(b Integer) {
 *a += b
}

This code is very simple, we define an Operation interface and implement two methods of Integer type. O n the face of it everything is fine, but there is one detail. Less less and Add methods are different for the type, Less method we don't need to modify the original value, so we pass in the value of Integer and Add method, we need to modify the original value, so the type we pass in is the pointer to Integer

So the question is, the two methods are of different types, can we assign its value to Operation interface I f so, should we pass a value or a pointer? Which of the second and third lines of code is correct?

var a Integer = 1
var b Operation = &a
var b Operation = a

The answer is that the second line is correct for a simple reason, because after we pass in the pointer, golang compiler automatically generates a new Less method. Calling the original method in this converted type method is equivalent to making a layer of transit.

func (a *Integer) Less(b Integer) bool{
    return (*a).Less(b)
}

So what's the other way around? We also write the code:

func (a Integer) Add (b Integer) {
    (&a).Add(b)
}

Obviously this is not possible, because the function can only be modified after execution by the value of a parameter in Add method, and there is no way to modify the original value. This doesn't match what we want, so golang didn't choose this strategy.

(Recommended micro-class: Go micro-class)

summary

In today's article, we describe some of the advanced uses of interface in golang such as using it as a universal type to receive values in various formats. For interface the problem of empty pointer calls to interface, and the problem of inconsistent receive types of two functions in interface

That is to say, in the go language, interface is both a polymorphic implementation specification and an all-powerful type derived from the function, this design is really amazing. T he skilled use of interface can greatly reduce the complexity of our coding and the efficiency of our operations among some issues. T his is also one of golang native advantages. I hope the above related introduction can be helpful to everyone.

Source: www.toutiao.com/a6859567247216771587/