zoukankan      html  css  js  c++  java
  • golang(6): 接口 & 反射

    接口详解

    // 举例:sort包中的 Sort 函数,如下:
    func Sort(data Interface)
    Sort sorts data. It makes one call to data.Len to determine n, and O(n*log(n)) calls to data.Less and data.Swap. The sort is not guaranteed to be stable.
    (Sort 对 data 进行排序。 它调用一次 data.Len 来决定排序的长度 n,调用 data.Less 和 data.Swap 的开销为 O(n*log(n))。此排序为不稳定排序。)
    
    type Interface interface {
            // Len is the number of elements in the collection.
            Len() int
            // Less reports whether the element with
            // index i should sort before the element with index j.
            Less(i, j int) bool
            // Swap swaps the elements with indexes i and j.
            Swap(i, j int)
    }

    举例来扩展 Sort 函数的功能,代码如下:

    package main
    
    import (
        "fmt"
        "sort"
        "math/rand"
    )
    
    type Student struct {
        Name string
        Id string
        Age int
    }
    
    type StudentSlice []Student        // 自定义一个切片类型 StudentSlice,切片中的元素是 Student 类型
    
    func (p StudentSlice) Len() int {
        return len(p)
    }
    
    func (p StudentSlice) Less(i, j int) bool {
        return p[i].Name < p[j].Name
    }
    
    func (p StudentSlice) Swap(i, j int) {
        p[i], p[j] = p[j], p[i]
    }
    
    // StudentSlice 类型完全实现了 sort.Sort() 形参中的 Interface 类型,那么 就可以用 sort.Sort() 函数对 StudentSlice 类型的数据进行排序
    
    func main(){
        var stuslice StudentSlice 
        for i := 0; i < 10; i++ {
            stu := Student{        // 生成 Student 类型的结构体
                Name: fmt.Sprintf("stu%d",rand.Intn(100)),
                Id:fmt.Sprintf("110%d",rand.Int()),
                Age: rand.Intn(100),
            }
            stuslice = append(stuslice,stu)        // Student 类型的结构体添加到 stuslice 切片中
        }
    
        for _, v := range stuslice {
            fmt.Println(v)
        }
    
        fmt.Println("
    ")
    
        sort.Sort(stuslice)        // 由于stuslice 类型实现了Interface这个接口,那么就能调用 sort.Sort() 对其进行排序
    
        for _, v := range stuslice {
            fmt.Println(v)
        }
    }
    
    
    // 编译后运行结果如下:
    [root@NEO project]# go build -o bin/example01_interface_extend go_dev/day06/example01_interface_extend/main
    [root@NEO project]# bin/example01_interface_extend 
    {stu81 1108674665223082153551 47}
    {stu59 1103916589616287113937 18}
    {stu25 1101443635317331776148 56}
    {stu0 1104751997750760398084 11}
    {stu62 1103510942875414458836 28}
    {stu74 1102610529275472644968 45}
    {stu37 1102015796113853353331 95}
    {stu66 1105263531936693774911 58}
    {stu47 1102740103009342231109 87}
    {stu88 1107981306761429961588 15}
    
    
    {stu0 1104751997750760398084 11}
    {stu25 1101443635317331776148 56}
    {stu37 1102015796113853353331 95}
    {stu47 1102740103009342231109 87}
    {stu59 1103916589616287113937 18}
    {stu62 1103510942875414458836 28}
    {stu66 1105263531936693774911 58}
    {stu74 1102610529275472644968 45}
    {stu81 1108674665223082153551 47}
    {stu88 1107981306761429961588 15}
    [root@NEO project]# 
    
    // 任何类型,只要实现了它的规范(接口),就可以调用它的函数来排序

    断言

    // 示例代码:
    package main
    
    import (
        "fmt"
    )
    
    func main(){
        var a interface{}    // 定义一个空接口
        var b int
        a = b
        c := a.(int)    // 断言;把 接口a 转成 int 型
        fmt.Printf("%d %T
    ",c,c)
    
        d,ok := a.(string)        // 断言(加判断);检查是否能转成 string 类型,并带判断
        if ok == false {
            fmt.Println("convert failed")
            return 
        }
        fmt.Println("%d :string type",d)
    }
    
    // 运行结果:
    [root@NEO example01_interface_assert]# go run main/main.go
    0 int
    convert failed
    [root@NEO example01_interface_assert]# 

    类型断言:(采用type switch方式)

    写一个函数判断传入参数的类型

    // 示例代码:
    package main
    
    import "fmt"
    
    func classifier(items...interface{}){
        for i,x := range(items){
            switch x.(type){    // 接口.(type) 要和 switch 一起用
                case bool:
                    fmt.Printf("param #%d is a bool,val is %v
    ", i,x)
                case float64:
                    fmt.Printf("param #%d is a float64,val is %v
    ", i,x)
                case int,int64:
                    fmt.Printf("param #%d is a int,val is %v 
    ",i,x)
                case nil:
                    fmt.Printf("param #%d is nil,val is %v
    ", i,x)
                case string:
                    fmt.Printf("param #%d is a string,val is %v
    ", i,x)
                default:
                    fmt.Printf("param #%d’s type is unknown,val is %v
    ", i,x)
            }
        }
    }
    
    func main() {
        var a int
        classifier(a,"abc",3.3)
    }
    
    
    // 运行结果:
    [root@NEO example01_interface_assert02]# go run main/main.go
    param #0 is a int,val is 0 
    param #1 is a string,val is abc
    param #2 is a float64,val is 3.3
    [root@NEO example01_interface_assert02]# 

    判断一个变量是否实现了指定接口

    // 示例代码:
    package main
    
    import "fmt"
    
    type Stringer interface {
        Strings() string
    } 
    
    type MyStruct struct{
        Name string
        Id int
    }
    
    func (p *MyStruct) Strings() string{
        return p.Name
    }
    
    func main() {
        var vp *MyStruct = &MyStruct{
            Name:"mystruct",
            Id: 110,
        }
    
        var t interface{}
        t = vp    // 要判断变量是否实现了某个接口,必须要选择该变量赋值给一个接口再调用下面的方法
    
        if v,ok := t.(Stringer);ok {        // 此处只能 t.(Stringer),即只能用接口去调用 .(接口名) (把变量赋值给接口再判断);返回两个值:接口t指向的变量 和 判断结果(true 或 false)
            fmt.Printf("vp implements Strings():%s; v:%v; ok:%v
    ",v.Strings(),v,ok)
        }
    }
    
    // 运行结果:
    [root@NEO example01_interface_var2interface]# go run main/main.go
    vp implements Strings():mystruct; v:&{mystruct 110}; ok:true
    [root@NEO example01_interface_var2interface]# 

    实现一个通用的链表类

    // 目录结构如下:
    example01_interface_linkedlist
    ├── linkedlist.go
    └── main.go
    
    // linkedlist.go 代码如下:
    package main
    
    import "fmt"
    
    type LinkNode struct {    // 定义一个节点类
        data interface{}    // 因为 data 要存任何类型的数据,所以要用空接口
        next *LinkNode
    }
    
    type Linkedlist struct{        // 定义一个链表类
        head *LinkNode        // 定义链表头节点
        tail *LinkNode        // 定义链表尾节点
    }
    
    
    func (p *Linkedlist) HeadInsert(data interface{}){        //头插法; data 要能够接收任何类型的数据,所以要用接口类型
        node := &LinkNode{        // 先生成一个节点
            data:data,
            next:nil,
        }
    
        if (p.head == nil && p.tail == nil){    // 此时链表为空
            p.tail = node
            p.head = node      // 头尾节点此时都赋值为 node
            return
        }
    
        node.next = p.head         // 链表头作为新生成节点的 next
        p.head = node        // 新的节点成为链表头
    }
    
    func (p *Linkedlist) TailInsert(data interface{}){        // 尾插法
        node := &LinkNode{      // 先生成一个节点
            data:data,
            next:nil,
        }
    
        if (p.head == nil && p.tail == nil){    // 此时链表为空
            p.tail = node
            p.head = node        // 头尾节点此时都赋值为 node
            return 
        }
    
        p.tail.next = node        // 新生成节点先成为原尾节点的 next
        p.tail = node     // 新节点成为尾节点
    }
    
    
    func (p *Linkedlist) Traversal(){    // 遍历链表
        node := p.head
        for (node != nil){
            fmt.Println(node.data)
            node = node.next
        }
    }
    
    
    // main.go 代码如下:
    package main
    
    func main(){
        var intLink *Linkedlist = new(Linkedlist)
        for i := 0; i < 10; i++{
        //    intLink.HeadInsert(i)
            intLink.TailInsert(i)
        }
        
        intLink.Traversal()
    }
    
    
    // 头插法编译后运行结果如下:
    [root@NEO project]# go build -o bin/example01_interface_linkedlist go_dev/day06/example01_interface_linkedlist
    [root@NEO project]# bin/example01_interface_linkedlist
    9
    8
    7
    6
    5
    4
    3
    2
    1
    0
    [root@NEO project]# 

    interface{},接口中一个方法也没有,所以任何类型都实现了空接口,也就是任何变量都可以赋值给空接口。

    变量slice和接口slice之间赋值操作,要用for range 一个个元素赋值

    var a []int
    var b []interface{}
    // b = a    // 不能直接 a 赋值给了

    反射

    // 反射:可以在运行时动态获取变量的相关信息
        Import ("reflect")
        
    反射相关函数:
    reflect.TypeOf                    // 获取变量的类型,返回reflect.Type类型
    reflect.ValueOf                    // 获取变量的值,返回reflect.Value类型
    reflect.Value.Kind                // 获取变量的类别,返回一个常量
    reflect.Value.Interface()        //  转换成interface{}类型
    
    // 变量  <--> Interface{} <--> reflect.Value 

    示例代码:

    package main
    
    import (
        "fmt"
        "reflect"
    )
    
    type Student struct{
        Name string
        Age int
        Score float64
    }
    
    func test(b interface{}){
        t := reflect.TypeOf(b)        // 获取变量的类型
        fmt.Println(t)    // main.Student 类型
    
        v := reflect.ValueOf(b)        // 返回 reflect.Value 类型,可利用这个 Value 类型 及相对应的方法 对这个变量进行更深入的分析( 是浮点型,就能获取浮点型的值,int型能获取int的值)
        fmt.Println(v)     // {stu01 18 80}
    
        k := v.Kind()    // 返回 reflect.Value 的类别
        fmt.Println(k)    // struct
    
        iv := v.Interface()        // 再把 reflect.Value 类型转化为 接口类型
        stu, ok := iv.(Student)        // 判断 iv 是不是指向 Student 类型;如果是 就转化为 Student 类型的值
        if ok == true {
            fmt.Printf("%v %T
    ",stu,stu)        // 打印结果: {stu01 18 80} main.Student
        }
    }
    
    
    func TestInt(b interface{}){
        v := reflect.ValueOf(b)
        val := v.Int()        // 获取int值(如果是 int类型)
        fmt.Printf("get value of interface{} %d
    ",val)        // 打印结果: get value of interface{} 123
    }
    
    func main(){
        var stu Student = Student{
            Name:"stu01",
            Age:18,
            Score:80,
        }
        test(stu)
    
        TestInt(123)
    }
    
    // 运行结果:
    [root@NEO example02_reflect_01]# go run main/main.go
    main.Student
    {stu01 18 80}
    struct
    {stu01 18 80} main.Student
    get value of interface{} 123
    [root@NEO example02_reflect_01]# 

    reflect.Value.Kind()方法返回的常量

    const (
        Invalid Kind = iota
        Bool
        Int
        Int8
        Int16
        Int32
        Int64
        Uint
        Uint8
        Uint16
        Uint32
        Uint64
        Uintptr
        Float32
        Float64
        Complex64
        Complex128
        Array
        Chan
        Func
        Interface
        Map
        Ptr
        Slice
        String
        Struct
        UnsafePointer
    )

    获取变量的值:

    reflect.ValueOf(x).Float()         // 获取 float 型的值
    reflect.ValueOf(x).Int()        // 获取 int 型的值
    reflect.ValueOf(x).String()        // 获取 string 型的值
    reflect.ValueOf(x).Bool()        // 获取 bool 型的值

    通过反射的来改变变量的值:

    reflect.Value.SetXX 相关方法,比如:
        reflect.Value.SetFloat()        // 设置浮点数
        reflect.Value.SetInt()            // 设置整数
        reflect.Value.SetString()        // 设置字符串

    示例代码:

    package main
    
    import (
        "fmt"
        "reflect"    
    )
    
    func SetVal(data interface{}){
        v := reflect.ValueOf(data)    // SetVal(&b) 传入的是一个指针,获取到的 val 也是一个指针(指针型的Value)
        fmt.Println(v)
        v.Elem().SetInt(10)        // v.Elem() 作用即 相当于 *指针 ,即该指针指向变量的 Value (reflect.Value 型,可理解为一个结构体); 把值设置成 10;由于v是一个指针,此处不能直接用 v.SetInt()
        
    }
    
    func main(){
        var b int = 123
        SetVal(&b)        // 此处不能只传入 b,只传b传入的是复本,修改复本不会改变原来值的大小, 调用 SetInt()时会 panic
        fmt.Println(b)
    }
    
    // 运行结果:
    [root@NEO example02_reflect02_setval]# go run main/main.go
    0xc000016098
    10
    [root@NEO example02_reflect02_setval]# 

    用反射操作结构体

    reflect.Value.NumField()        // 获取结构体中字段的个数
    reflect.Value.Method(n).Call    // 来调用结构体中的方法(n表示下标,可理解成第n个方法);.Call 的参数是 reflect.Value 型的切片,返回值也是 reflect.Value 型的切片

    示例代码:

    package main
    
    import (
        "fmt"
        "reflect"
    )
    
    type Student struct{
        Name string `json:"student_name"`
        Age int
        Score float64
    }
    
    func (p *Student) Print(){
        fmt.Println("*p ------->",*p)
    }
    
    func (p *Student) Set(name string,age int,score float64){
        p.Name = name
        p.Age = age
        p.Score = score
    }
    
    func HandleStruct(data interface{}){
        fmt.Printf("v:%v T:%T
    ",data,data)
        tye := reflect.TypeOf(data)        // 指针型的 reflect.Type
    
        v := reflect.ValueOf(data)
        fmt.Printf("value:%v type:%T
    ",v,v)
        kd := v.Elem().Kind()
        if kd != reflect.Struct {    // Struct 是 reflect 这个包中的常量;先判断 v的类别 是不是 结构体类型
            fmt.Println("struct expected")
            return
        }
        
        numField := v.Elem().NumField()        // reflect.Value.NumField()    ---> 获取结构体字段的个数
        fmt.Println("struct field number:",numField)
    
        for i := 0; i < v.Elem().NumField();i++ {   // 获取字段的值: reflect.Value.Field(i)
            fmt.Printf("i: %d  v_T: %T  v: %v
    ",i, v.Elem().Field(i).Kind(), v.Elem().Field(i))
        }
    
        // 获取tag (json.Marshal()的原理)
        tag := tye.Elem().Field(0).Tag.Get("json")        // tye.Elem() ---> 指针指向变量的 reflect.Type
        fmt.Println("tag:",tag)
    
    
        numMethod := v.NumMethod()        // reflect.Value.NumMethod()  ---> 获取结构体的方法数;由于 Set 传入的是 Student 的指,所以是 v.Set()
        fmt.Println("struct method number:",numMethod)    
    
        v.Method(0).Call(nil)    // 传入 nil
    
        params := make([]reflect.Value,3)    // 声明并初始化 reflect.Value 型的切片(该切片要作为 .Call() 的参数)
        params[0] = reflect.ValueOf("neo")    // 任何类型的数据经过 reflect.ValueOf()处理之后都会得到相应的 reflect.Value 类型
        params[1] = reflect.ValueOf(22)
        params[2] = reflect.ValueOf(98.5)
        v.Method(1).Call(params)
        fmt.Printf("*data v:%v *data T:%T data v:%v data T:%T
    ",data,data,*(data.(*Student)),data.(*Student))
        // 形参 data 是一个接口,调用 HandleStruct() 函数时传入的 &stu 是一个指针,data 是一个指向指针的接口,接口 data 前不能直接加 * 取值, data.(*Student) 通过断言 获取到 data的指针型数据,再利用 *指针 获取指针指向的值 (应该也可以通过 reflect.ValueOf(data).Elem() 来获取指针指向变量的Value型,然后再获取其值)
    }
    
    func main(){
        var stu Student = Student{
            Name:"stu01",
            Age:18,
            Score:85.5,
        }
        HandleStruct(&stu)
    }
    
    
    // 运行结果:
    [root@NEO example02_reflect_handleStruct]# go run main/main.go
    v:&{stu01 18 85.5} T:*main.Student
    value:&{stu01 18 85.5} type:reflect.Value
    struct field number: 3
    i: 0  v_T: reflect.Kind  v: stu01
    i: 1  v_T: reflect.Kind  v: 18
    i: 2  v_T: reflect.Kind  v: 85.5
    tag: student_name
    struct method number: 2
    *p -------> {stu01 18 85.5}
    *data v:&{neo 22 98.5} *data T:*main.Student data v:{neo 22 98.5} data T:*main.Student
    [root@NEO example02_reflect_handleStruct]# 
  • 相关阅读:
    HDU 2544 最短路
    HDU 3367 Pseudoforest
    USACO 2001 OPEN
    HDU 3371 Connect the Cities
    HDU 1301 Jungle Roads
    HDU 1879 继续畅通工程
    HDU 1233 还是畅通工程
    HDU 1162 Eddy's picture
    HDU 5745 La Vie en rose
    HDU 5744 Keep On Movin
  • 原文地址:https://www.cnblogs.com/neozheng/p/11273687.html
Copyright © 2011-2022 走看看