Data structure and algorithm (Golang implementation) (5) A simple introduction to Golang-interface

interface

In the Golangworld, there is interfacesomething called magic.

First, the data type interface {}

If you don't know what data type a variable is before, whether it is an integer or a string, but you still want to use it.

Golanginterface{}A data type called "name" is generated , indicating that it does not know what type it is. for example:

package main

import (
    "fmt"
    "reflect"
)

func print(i interface{}) {
    fmt.Println(i)
}

func main() {
    // 声明一个未知类型的 a,表明不知道是什么类型
    var a interface{}
    a = 2
    fmt.Printf("%T,%v\n", a, a)

    // 传入函数
    print(a)
    print(3)
    print("i love you")

    // 使用断言,判断是否是 int 数据类型
    v, ok := a.(int)
    if ok {
        fmt.Printf("a is int type,value is %d\n", v)
    }

    // 使用断言,判断变量类型
    switch a.(type) {
    case int:
        fmt.Println("a is type int")
    case string:
        fmt.Println("a is type string")
    default:
        fmt.Println("a not type found type")
    }

    // 使用反射找出变量类型
    t := reflect.TypeOf(a)
    fmt.Printf("a is type: %s", t.Name())
}

Output:

int,2
2
3
i love you
a is int type,value is 2
a is type int
a is type: int

1.1. Basic use

We use interface{}to declare a variable of unknown type a:

    // 声明一个未知类型的 a,表明不知道是什么类型
    var a interface{}
    a = 2
    fmt.Printf("%T,%v\n", a, a)

Then assign an integer to the variable: a=2At this time, ait is still an unknown type. Use the placeholder %Tto print the true type of the variable, and the placeholder to %vprint the value. At this time fmt.Printf, the type will be judged internally.

We can also set the parameters of the function interfaceas the same as the definition of the variable:

func print(i interface{}) {
    fmt.Println(i)
}

when using it:

    // 传入函数
    print(a)
    print(3)
    print("i love you")

printThe parameters we pass into the function can be of any type, such as integers 3or strings i love you. After entering the function, the variable in the function iloses its type and is an unknown type. This feature makes it unnecessary for us to write multiple functions if we want to process different types of data.

Of course, the fields in the structure can also be interface{}:

type H struct {
    A interface{}
    B interface{}
}

1.2. Determine the specific type

We defined it interface{}, but in actual use, we have a need to judge the type. There are two ways to judge.

Use assertions:

    // 使用断言,判断是否是 int 数据类型
    v, ok := a.(int)
    if ok {
        fmt.Printf("a is int type,value is %d\n", v)
    }

Used directly after the variable .(int), two return values v, okwill be returned. okIf it trueindicates that it is indeed an integer type, this integer will be assigned v, and then we can vplay happily. Otherwise, okas false, vto a null value, which is the default value of 0.

If we use it this way every time, it will be uncomfortable, because a interface{}type of variable, the data type may be .(int), may be .(string), can be used switchto simplify:

    // 使用断言,判断变量类型
    switch a.(type) {
    case int:
        fmt.Println("a is type int")
    case string:
        fmt.Println("a is type string")
    default:
        fmt.Println("a not type found type")
    }

In swicth, the assertion is no longer used .(具体类型), but a.(type).

Finally, there is a way to use the reflection package reflectto determine the data type:

    // 使用反射找出变量类型
    t := reflect.TypeOf(a)
    fmt.Printf("a is type: %s", t.Name())

This package will directly use non-secure pointers to get the real data type:

func TypeOf(i interface{}) Type {
    eface := *(*emptyInterface)(unsafe.Pointer(&i))
    return toType(eface.typ)
}

For general daily development, reflection packages are rarely used.

2. Interface structure

We are all functional programming or structural method programming now. Isn't there the object-oriented, object inheritance characteristic of other languages? Yes, the Golanglanguage is called interface-oriented programming.

package main

import (
    "fmt"
    "reflect"
)

// 定义一个接口,有一个方法
type A interface {
    Println()
}

// 定义一个接口,有两个方法
type B interface {
    Println()
    Printf() int
}

// 定义一个结构体
type A1Instance struct {
    Data string
}

// 结构体实现了Println()方法,现在它是一个 A 接口
func (a1 *A1Instance) Println() {
    fmt.Println("a1:", a1.Data)
}

// 定义一个结构体
type A2Instance struct {
    Data string
}

// 结构体实现了Println()方法,现在它是一个 A 接口
func (a2 *A2Instance) Println() {
    fmt.Println("a2:", a2.Data)
}

// 结构体实现了Printf()方法,现在它是一个 B 接口,它既是 A 又是 B 接口
func (a2 *A2Instance) Printf() int {
    fmt.Println("a2:", a2.Data)
    return 0
}

func main() {
    // 定义一个A接口类型的变量
    var a A

    // 将具体的结构体赋予该变量
    a = &A1Instance{Data: "i love you"}
    // 调用接口的方法
    a.Println()
    // 断言类型
    if v, ok := a.(*A1Instance); ok {
        fmt.Println(v)
    } else {
        fmt.Println("not a A1")
    }
    fmt.Println(reflect.TypeOf(a).String())

    // 将具体的结构体赋予该变量
    a = &A2Instance{Data: "i love you"}
    // 调用接口的方法
    a.Println()
    // 断言类型
    if v, ok := a.(*A1Instance); ok {
        fmt.Println(v)
    } else {
        fmt.Println("not a A1")
    }
    fmt.Println(reflect.TypeOf(a).String())

    // 定义一个B接口类型的变量
    var b B
    //b = &A1Instance{Data: "i love you"} // 不是 B 类型
    b = &A2Instance{Data: "i love you"}
    fmt.Println(b.Printf())
}

Output:

a1: i love you
&{i love you}
*main.A1Instance
a2: i love you
not a A1
*main.A2Instance
a2: i love you
0

We can define an interface type, use type 接口名 interface, this time is no longer interface{}:

// 定义一个接口,有一个方法
type A interface {
    Println()
}

// 定义一个接口,有两个方法
type B interface {
    Println()
    Printf() int
}

It can be seen that the interface Aand Bis an abstract structure. Each interface has some methods in it. As long as the structure structimplements these methods, then these structures are the type of this interface. Such as:

// 定义一个结构体
type A1Instance struct {
    Data string
}

// 结构体实现了Println()方法,现在它是一个 A 接口
func (a1 *A1Instance) Println() {
    fmt.Println("a1:", a1.Data)
}

// 定义一个结构体
type A2Instance struct {
    Data string
}

// 结构体实现了Println()方法,现在它是一个 A 接口
func (a2 *A2Instance) Println() {
    fmt.Println("a2:", a2.Data)
}

// 结构体实现了Printf()方法,现在它是一个 B 接口,它既是 A 又是 B 接口
func (a2 *A2Instance) Printf() int {
    fmt.Println("a2:", a2.Data)
    return 0
}

We require that the structure must implement certain methods, so we can define a variable of the interface type, and then assign the structure to it:

    // 定义一个A接口类型的变量
    var a A
    // 将具体的结构体赋予该变量
    a = &A1Instance{Data: "i love you"}
    // 调用接口的方法
    a.Println()

If the structure does not implement this method, the compilation will fail and the binary cannot be compiled.

Of course, you can also use assertions and reflection to determine which actual structure the interface type belongs to struct.

    // 断言类型
    if v, ok := a.(*A1Instance); ok {
        fmt.Println(v)
    } else {
        fmt.Println("not a A1")
    }
    fmt.Println(reflect.TypeOf(a).String())

GolangIt is very smart to determine whether the structure implements the interface method. If it is implemented, then the structure is the interface type. We flexibly use the characteristics of the interface structure, and use the combined form to develop more flexible programs.

Series article entry

I am the star Chen, Welcome I have personally written data structures and algorithms (Golang achieve) , starting in the article to read more friendly GitBook .

Published 13 original articles · praised 0 · visits 94

Guess you like

Origin blog.csdn.net/m0_46803965/article/details/105563303