七叶笔记 » golang编程 » GOLANG语言基础知识(五)接口和反射知识速记(不断更新)

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

​一、接口

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

格式如下:

type interfaceName interface{

methodName(param paramType)returnType

methodName(param paramType…)(returnValue returnType…)

}

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

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

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

 //定义一个接口
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())
}  

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

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

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

 b := &Book{}
b.Price = 10
GetPrice(b)  

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

 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  

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

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

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

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

 l := Laptop{}
l.Price=100
l.GetPrice()  

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

 type stu struct {  
  Name string  
  Score int
}  
  • reflect.TypeOf(interface{})的使用
 //直接打印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  
  • reflect.ValueOf(interface{})的使用,包含原值
  • 通过反射获取变量的值
 //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)  //芳芳  
  }
}  
  • 通过反射修改变量的值;

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

 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;i++{    
    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)  

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

相关文章