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]# 
  • 相关阅读:
    css3 边框、背景、文本效果
    Java JDBC连接MYSQL数据库教程
    waf平台常用方法总结
    java比较两个日期大小
    js控制的弹出层
    js时间大小判断写法demo
    PL/SQL Developer技巧
    杀Oracle死锁进程方法
    查看oracle数据库的连接数以及用户
    Oracle分散问题记录
  • 原文地址:https://www.cnblogs.com/neozheng/p/11273687.html
Copyright © 2011-2022 走看看