Interface of go language defragmentation

Posted by Rohlan on Wed, 26 Jun 2019 21:08:45 +0200

Interface

interface defines the behavior specification of an object, only defines the specification but not implements it. Specific objects are used to implement the details of the specification.

Interface type

In Go language, interface is a kind of type and an abstract type.
Interface is a set of methods, which is an embodiment of duck-type programming. What the interface does is define a protocol (rule). As long as a machine has the function of washing and drying, I call it a washing machine. Do not care about attributes (data), only about behavior (methods).
To protect your career in the Go language, keep in mind that interface is a type.

Why use interfaces

package main
import (
    "fmt"
)
type Cat struct{}
func (c Cat)Say() string{
    return "cat"
}
type Dog struct{}
func (d Dog)Say() string{
    return "Wang Wang Wang"
}
func main(){
    c := Cat{}
    fmt.Println("cat:",c.Say())
    d := Dog{}
    fmt.Println("Dog:",d.Say())
}

The above code defines cats and dogs, and then they all call. You will notice that there is obviously duplication of code in the main function. If we add pigs and frogs later, our code will continue to repeat. Can we treat them as "callable animals"?
Similar examples are often encountered in our programming process:
For example, an online shopping mall may use Alipay, WeChat and UnionPay to pay online. Can we treat them as "payment methods"?
For example, triangles, quadrilaterals and circles can calculate circumference and area. Can we treat them as "graphics"?
For example, sales, administration and programmers can calculate monthly salaries. Can we treat them as "employees"?
In order to solve the above problems, the concept of interface is designed in Go language. Interfaces are different from all the concrete types before us. Interfaces are abstract types. When you see the value of an interface type, you don't know what it is. The only thing you know is what you can do through its methods.

Definition of interface

The Go language advocates Interface-oriented programming.
Each interface consists of several methods. The interface is defined in the following format:

Type interface type name interface{
    Method name 1 (parameter list 1) return value list 1
    Method name 2 (parameter list 2) return value list 2
    ...
}

Among them:

  • Interface name: Use type to define the interface as a custom type name. When the interface of Go language is named, it usually adds er after the word. For example, the interface with write operation is called writer, and the interface with string function is called Stringer, etc. The interface name is best used to highlight the type meaning of the interface.
  • Method Name: When the method name is capitalized and the interface type is capitalized, the method can be accessed by code other than the package where the interface is located.
  • Parameter list, return value list: parameter variable names in parameter list and return value list can be omitted.

For instance:

type writer interface{
    Write([]byte) err
}

When you see the value of this interface type, you don't know what it is, but the only thing you know is that you can do something with its Write method.

Conditions for implementing interfaces

As long as an object implements all the methods defined in the interface, it is the interface. Let's define an Animal interface:

type Sayer interface{
    say()
}

Define two structures of dog and cat

type dog struct{}

type cat struct{}

Because there is only one say method in the Sayer interface, we only need to implement the say method for the dog and cat respectively to implement the Sayer interface.

//dog implements Sayer interface
func (d dog) say(){
    fmt.Println("Wang Wang Wang")
}
//cat implements Sayer interface
func (c cat) say(){
    fmt.Println("cat")
}

The implementation of the interface is so simple, as long as all the methods in the interface are implemented, the interface is realized.

Interface type variable

So what's the use of implementing interfaces?
Interface type variables can store all instances that implement the interface. For example, in the example above, Sayer type variables can store variables of type dog and cat.

func main(){
    var x Sayer
    a := cat{}
    b := dog{}
    x =a 
    x.say()
    x = b
    x.say()
}

The Difference between Value Receiver and Pointer Receiver in Implementing Interfaces

What's the difference between using a value receiver to implement an interface and using a pointer receiver to implement an interface? Next, let's take an example to see the difference.
We have a Mover interface and a dog structure.

type Mover interface{
    move()
}
type dog struct{}

Value Receiver Implementation Interface

func (d dog)move(){
    fmt.Println("Dogs move")
}

At this point, the implementation of the interface is the dog type:

func main(){
    var x Mover
    var wangcai = dog{}
    x = wangcai
    var fugui = &dog{}
    x = fugui
    x.move()
}

From the above code, we can see that after the interface is implemented with the value receiver, variables of either the dog structure or the pointer dog type of the structure can be assigned to the interface variable. Because there are grammatical sugars for evaluating pointer-type variables in Go, the dog pointer fugui automatically evaluates fuzhi.

Pointer Receiver Implementation Interface

In the same code, let's test the difference between using pointer receivers:

func(d *dog)move(){
    fmt.Println("Dogs move")
}
func main(){
    var x Mover
    var wangcai = dog{}
    x = wangcai
    var fugui = &dog{}
    x = fugui
}

At this point, the implementation of the Mover interface is of the * dog type, so it is not possible to pass in the wangcai of the dog type to x, where x can only store the value of the ** dog type.

The relationship between types and interfaces

A type can implement multiple interfaces at the same time, but interfaces are independent of each other and do not know each other's implementation. For example, a dog can bark or move. We define the Sayer interface and the Over interface respectively, as follows: the Mover interface.

type Sayer interface{
    say()
}
type Mover interface{
    move()
}

dog can implement both Sayer interface and Mover interface.

type Sayer interface{
    say()
}
type Mover interface{
    move()
}
type dog struct{
    name string
}
func (d dog)say(){
    fmt.Printf("%s Can call Wang Wang Wang\n",d.name)
}
func (d dog)move(){
    fmt.Printf("%s Will move\n",d.name)
}
func main(){
    var x Sayer
    var y Mover
    var a = dog{
        name:"Prosperous wealth"
    }
    x = a
    y = a
    x.say()
    y.move()
}

Multiple types implement the same interface
Different types of GO language can also implement the same interface. First, we define a Mover interface, which must have a move method.

type dog struct{
    name string
}
type car struct{
    brand string
}
func (d dog)move(){
    fmt.Printf("%s Can run\n",d.name)

}
func (c car)move(){
    fmt.Printf("%s 70 mph\n",c.brand)
}
func main(){
    var x Mover
    var a = dog{name:"Prosperous wealth"}
    var b = car{brand:"BMW"}
    x = a
    x.move()
    x = b
    x.move()
}

The bands above are executed as follows:

Booming Finance and Accounting
 BMW is 70 mph

Moreover, an interface method does not necessarily require a complete implementation of a type. The interface method can be implemented by embedding other types or structures in the type.

type washingmachine interface{
    wash()
    dry()
}
type dryer struct{}
func (d dryer) dry(){
    fmt.Println("Shake it off.")
}
type haier struct{
    dryer
}
func (h haier)wash(){
    fmt.Println("Break Remix")
}

Interface nesting

New interfaces can be created by nesting between interfaces.

type Sayer interface{
    say()
}
type Mover interface{
    move()
}
type animal interface{
    Sayer
    Mover
}

Nested interfaces are used as normal interfaces. Here we let cat implement animal interfaces:

type Sayer interface{
    say()
}
type Mover interface{
    move()
}
type animal interface{
    Sayer
    Mover
}
type cat struct {
    name string
}
func (c cat)say(){
    fmt.println("cat")
}
func (c cat)move(){
    fmt.Println("Cats move")
}
func main(){
    var x animal
    x = cat{name:"tearful"}
    x.move()
    x.say()
}

Empty interface

Definition of empty interface

Variables of empty interface type can store any type of variable.

func main(){
    var x interface{}
    s := "hello Shahe"
    x = s
    fmt.Printf("type:%T value:%v\n",x,x)
    i :=100
    x = i
    fmt.Printf("type:%T value:%v\n", x,x)
    b := true
    x = b
    fmt.Printf("type:%T value:%v\n", x,x )
}

Application of Air Interface

Empty interface as parameter of function
Empty interfaces are used to realize the acceptance of any type of function parameters.

func show(a interface{}){
    fmt.Printf("type:%T value:%v\n", a,a)
}

Empty interface as map value
A dictionary with arbitrary values can be saved using an empty interface.

var studentInfo = make(map[string]interface{})
studentInfo["name"] = "Shahe Naza"
studentInfo["age"] = 18
studentInfo["married"] = false
fmt.Println(studentInfo)

Type Asserts

An empty interface can store any type of value, so how do we get the specific data it stores?

Interface value

The value of an interface (referred to as interface value) is composed of two parts: "a specific type" and "a specific type of value". These two parts are called "dynamic type" and "dynamic value" of the interface respectively.
Let's take a concrete example:

var w io.Writer
w = os.Stdout
w = new(bytes.Buffer)
w = nil

To determine the value of an empty interface, you can use type assertions in the following grammatical format:

x.(T)

Among them:
x represents a variable of type interface {}.
T denotes the possible type of assertion x.
The grammar returns two parameters. The first parameter is the variable after x is converted to T type. The second value is a Boolean value. If true, the assertion succeeds. If false, the assertion fails.
For instance:

func main(){
    var x interface{}
    x = "hello Shahe"
    v,ok := x.(string)
    if ok {
        fmt.Println(v)
    }else {
        fmt.Println("Type assertion failed")
    }
}

In the example above, if you need to assert multiple times, you need to write multiple if judgements. At this time, we can use switch statement to achieve:

func justifyType(x interface{}){
    switch v := x.(type){
    case string:
        fmt.Printf("x is a string; value is %v\n",v)
    case int:
        fmt.Printf("x is a int,value is %v\n",v)
    case bool:
        fmt.Printf("x is a bool, value is %v\n",v)
    default:
        fmt.Printf("unsupport type!")
    }
}

Because the empty interface can store any type of value, it is widely used in Go language.
It is important to note that interfaces need to be defined only when two or more specific types have to be processed in the same way. Don't write interfaces for interfaces. That only adds unnecessary abstraction, resulting in unnecessary runtime wastage.

Topics: Go Programming