GOLANG语言基础知识(五)接口和反射知识速记(不断更新)

​一、接口

同其他面向对象的编程一样,只是定义一组规范,没有具体的实现,同一接口可以有多种不同的实现

格式如下:

type interfaceName interface{

methodName(param paramType)returnType

methodName(param paramType...)(returnValue returnType...)

}

在GO中没有类似于Object超类一说,如果说有的话,那就是interface{},

如果一个接口中没有任何方法(即空接口),那么任何类型都实现了这个接口,

如果一个类型要实现一个接口的话,那么需要实现这个接口的所有的方法。

<code>//定义一个接口
type Product interface {
GetPrice() int
}
//定义Computer
type Computer struct {
Price int
}
//Computer实现Product接口,使用值接收者
func (c Computer) GetPrice() int {
return c.Price
}
//定义Book
type Book struct {
Price int
}

//Book实现Product接口,使用指针接收者
func (b *Book) GetPrice() int {
return b.Price
}
//Laptop匿名结构体字段
type Laptop struct {
Computer
}
//可以使用接口类型作为参数
//那么实现这个接口的类型都符合作为参数的条件
func GetPrice(p Product) {
fmt.Println(p.GetPrice())
}/<code>

Computer实现接口用的是值接收者,所以此处无论声明值还是指针都可以,如果声明的是指针,由于Go语法糖的存在会自动把指针转化为值

<code>c := Computer{}//c := &Computer{}
c.Price = 10
GetPrice(c)/<code>

Book实现接口用的是指针接收者,所以必须需要声明指针,不能使用值声明,因为Go不能根据值找到对应的指针地址。

<code>b := &Book{}
b.Price = 10
GetPrice(b)/<code>

二、类型判断、类型断言,语法:t,ok := x.(T)

<code>var p Product
p = Computer{}
_, ok := interface{}(p).(Computer)
fmt.Println("computer is computer", ok)//true

_, ok = interface{}(p).(Product)
fmt.Println("computer is product", ok)//true
var i interface{}i = Laptop{}
_, ok = i.(Laptop)
fmt.Println("laptop is laptop", ok)//true
_, ok = i.(Computer)
fmt.Println("laptop is computer", ok)//false
_, ok = i.(Product)
fmt.Println("laptop is product", ok)//true/<code>

从上面可以看出GO可以通过组合来实现继承,其本质是基于匿名结构体字段

通过类型断言也进一步表明通过组合得到的继承是伪继承

Laptop虽然通过组合继承了Computer,获得了Computer字段和方法的能力

但是类型断言Laptop并不是Computer,而是Product。

<code>l := Laptop{}
l.Price=100
l.GetPrice()/<code>

三、反射可以动态获取类型的元数据信息,比如字段、方法等;也可以通过反射动态的修改类型实例的数据,GO提供了反射相关的工具在reflect包中。

<code>type stu struct {  
Name string
Score int
}/<code>
  • reflect.TypeOf(interface{})的使用
<code>//直接打印type会输出包名+类型名
fmt.Println(reflect.TypeOf(stu{}))//main.stu
fmt.Println(reflect.TypeOf("你好"))//string
fmt.Println(reflect.TypeOf([]rune("你好")))//[]int32
//打印type.Name能得到类型名,但是不包含包名;指针等引用类型的变量type.Name都是空字符串,如下:
ty := reflect.TypeOf(stu{})
fmt.Println(ty.Name(),ty.Kind())//stu struct
ty = reflect.TypeOf(&stu{})
fmt.Println(ty.Name()) //""
fmt.Println(ty.Kind(),reflect.Ptr==ty.Kind())//ptr true
ty = reflect.TypeOf(map[string]int{})
fmt.Println(ty.Name())//""
fmt.Println(ty.Kind())//map/<code>
  • reflect.ValueOf(interface{})的使用,包含原值
  • 通过反射获取变量的值
<code>//o := map[string]int{"1":1}
o := stu{"芳芳",99}
//o := 100
va := reflect.ValueOf(o)
if va.Kind()==reflect.Int {
fmt.Println(va.Int())
}else if va.Kind()==reflect.Map{
fmt.Println(va.MapKeys())
}else if va.Kind()==reflect.Struct {
fmt.Println("暴力",va.Interface().(stu))//{芳芳}
if s,ok:= va.Interface().(stu);ok{
fmt.Println(s.Name) //芳芳
}
}/<code>
  • 通过反射修改变量的值;

由于修改需要针传递,否则修改的就是其副本,所以在反射中需要修改值的时候一定要传入对应变量的指针,传入的指针通过Elem()方法获得其Value,否则可能会引发panic

<code>va = reflect.ValueOf(&o)// &重点
if va.Elem().Kind()==reflect.Int {
va.Elem().SetInt(200)
}else if va.Elem().Kind()==reflect.Map{ //map、chan、func、pointer、slice等
//引用类型可以调用判断引用是否为空,否则引发panic  
if !va.Elem().IsNil() {    //修改map的KV值    
va.Elem().SetMapIndex(reflect.ValueOf("1"),reflect.ValueOf(2))
}else{
fmt.Println("map is nil")
}
}else if va.Elem().Kind()==reflect.Struct {
//注意:如果想要修改私有变量(小写开头)则会抛异常
//panic: reflect: reflect.Value.SetInt using value obtained using unexported field
field := va.Elem().FieldByName("Score")  
//判断字段知否拥有,常用来判断返回值是否合法有效
if field.IsValid() {
field.Set(reflect.ValueOf(100))
}else{
fmt.Println("没有这个字段")
}
//遍历字段
fieldNum := va.Elem().Type().NumField()
for i:=0;i<fieldnum> field := va.Elem().Field(i)
if field.Type().Kind()==reflect.Int{
field.Set(reflect.ValueOf(1000))
}else if field.Type().Kind()==reflect.String{
field.Set(reflect.ValueOf("盈盈"))
}
}

}
fmt.Println("修改后",o)/<fieldnum>/<code>


让我们一起进步学习,共同成长;欢迎大家关注微信公众号:芝麻技术栈


分享到:


相關文章: