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("\n")

    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\n",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{}){  // func(items...) 表示可变长的参数; items 会成为切片类型
    for i,x := range(items){
        switch x.(type){    // 接口.(type) 要和 switch 一起用
            case bool:
                fmt.Printf("param #%d is a bool,val is %v\n", i,x)
            case float64:
                fmt.Printf("param #%d is a float64,val is %v\n", i,x)
            case int,int64:
                fmt.Printf("param #%d is a int,val is %v \n",i,x)
            case nil:
                fmt.Printf("param #%d is nil,val is %v\n", i,x)
            case string:
                fmt.Printf("param #%d is a string,val is %v\n", i,x)
            default:
                fmt.Printf("param #%d’s type is unknown,val is %v\n", 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\n",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\n",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\n",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\n",data,data)
    tye := reflect.TypeOf(data)        // 指针型的 reflect.Type

    v := reflect.ValueOf(data)
    fmt.Printf("value:%v type:%T\n",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\n",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() 和 v.Print()
    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\n",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]# 

 

posted @ 2019-07-31 00:58  neozheng  阅读(348)  评论(0)    收藏  举报