go语法专题:空接口,反射,空结构体,断言,泛型

参考:

https://blog.csdn.net/weixin_44014995/article/details/114596529(go语言的空接口,反射,泛型)

https://www.jianshu.com/p/6a46fc7b6e5b(go语言的类型断言)

前言

go语言就是通过接口interface{}和结构体struct{}组织起来的,interface{}是方法的集合,struct{}是数据结构的集合+接口的实现。

反射可以动态地获取任意对象的类型及其结构信息。

空接口的引入

Go语言打破了传统面向对象编程中类与类之间继承的概念,而是通过组合实现方法和属性的复用,所以不存在类似的继承关系数,也就没有所谓的祖宗类,而且类与接口之间也不再通过implements 关键字强制绑定实现关系,所以 Go 语言的面向对象编程非常灵活。

在Go语言中,类与接口的实现关系是通过类所实现的方法在编译期推断出来的,如果我们定义一个空接口的话,那么显然所有的类都实现了这个接口,反过来,我们也可以通过空接口来指向任意类型,从而实现类似Java中Object类所承担的功能,而且显然Go的空接口实现更加简洁,通过一个简单的字面量即可完成:

interface{}

 

空接口的基本使用

指向任意类型变量

#基本类型
var v1 interface{} = 1 // 将 int 类型赋值给 interface{} 
var v2 interface{} = "学院君" // 将 string 类型赋值给 interface{} 
var v3 interface{} = true  // 将 bool 类型赋值给 interface{}

#复合类型
var v4 interface{} = &v2 // 将指针类型赋值给 interface{} 
var v5 interface{} = []int{1, 2, 3}  // 将切片类型赋值给 interface{} 
var v6 interface{} = struct{   // 将结构体类型赋值给 interface{}
    id int
    name string
}{1, "学院君"} 

声明任意类型的参数

func Printf(fmt string, args ...interface{}) 
func Println(args ...interface{}) ...
func (p *pp) printArg(arg interface{}, verb rune)

 

反射

反射三大定律:

1,反射可以将interface变量转换成反射对象

2,反射可以将反射对象还原成interface变量

3,反射对象可修改,value值必须是可设置的

#第一定律
#TypeOf()和ValueOf()接受的参数都是interface{}类型的,也即x值是被转成了interface传入的。
var x float64 = 3.4
t := reflect.TypeOf(x)  //t is reflext.Type
fmt.Println("type:", t)

v := reflect.ValueOf(x) //v is reflext.Value
fmt.Println("value:", v)

#第二定律
#对象x转换成反射对象v,v又通过Interface()接口转换成接口对象,interface对象通过.(float64)类型断言获取float64类型的值。
var y float64 = v.Interface().(float64)
fmt.Println("value:", y)
#第三定律 var x float64 = 3.4 v := reflect.ValueOf(x) #不可修改 v.SetFloat(7.1) // Error: will panic. var x float64 = 3.4 v := reflect.ValueOf(&x) #可修改 v.Elem().SetFloat(7.1) fmt.Println("x :", v.Elem().Interface())

 

空结构体

另外,有的时候你可能会看到空的结构体类型定义:

struct{}

表示没有任何属性和成员方法的空结构体,该类型的实例值只有一个,那就是 struct{}{},这个值在 Go 程序中永远只会存一份,并且占据的内存空间是 0,当我们在并发编程中,将通道(channel)作为传递简单信号的介质时,使用 struct{} 类型来声明最好不过。

断言

参考:http://c.biancheng.net/view/4281.html

在Go语言中类型断言的语法格式如下:

value, ok := x.(T)

其中,x 表示一个接口的类型,T 表示一个具体的类型(也可为接口类型)。

该断言表达式会返回 x 的值(也就是 value)和一个布尔值(也就是 ok),可根据该布尔值判断 x 是否为 T 类型:

    • 如果 T 是具体某个类型,类型断言会检查 x 的动态类型是否等于具体类型 T。如果检查成功,类型断言返回的结果是 x 的动态值,其类型是 T。
    • 如果 T 是接口类型,类型断言会检查 x 的动态类型是否满足 T。如果检查成功,x 的动态值不会被提取,返回值是一个类型为 T 的接口值。
    • 无论 T 是什么类型,如果 x 是 nil 接口值,类型断言都会失败。

返回两个值

如果我们想知道类型断言是否失败,而不是失败时触发panic,可以使用返回两个值的版本:

y, ok := x.(T)
#举例
var w io.Writer = os.Stdout
f, ok := w.(*os.File) //成功:f为os.Stdout,ok为true
b, ok := w.(*bytes.Buffer) //失败:b为零值,这里是nil, ok为false,no panic

惯用:

if f, ok := w.(*os.File); ok {
    // ... use f ...
}

使用switch

func classifier(items ...interface{}) {
    for i, x := range items {
        switch x.(type) {
        case bool:
            fmt.Printf("Param #%d is a bool\n", i)
        case float64:
            fmt.Printf("Param #%d is a float64\n", i)
        case int, int64:
            fmt.Printf("Param #%d is a int\n", i)
        case nil:
            fmt.Printf("Param #%d is a nil\n", i)
        case string:
            fmt.Printf("Param #%d is a string\n", i)
        default:
            fmt.Printf("Param #%d is unknown\n", i)
        }
    }
}

 

posted @ 2021-11-04 16:12  小匡程序员  阅读(598)  评论(0编辑  收藏  举报