接口
支持匿名接口类型,可直接用为变量定义,或结构体字段
package main
type data struct {
}
func (data) string() string {
return "haha"
}
type node struct {
data interface {
string() string //匿名接口类型
}
}
func main() {
var t interface {
string() string //定义匿名接口变量
} = data{}
n := node{
data: t,
}
println(n.data.string())
}
如果实现接口的类型支持,可做相等运算
使用反射机制,编写函数的适配器,桥连接**
test1 := func(v1 int, v2 int){
t.Log(v1,v2)
}
基本介绍
反射可以在运行时动态获取变量的各种信息,比如变量的类型,类别
- 如果是结构体变量,还可以获取到结构体本身的信息(包括字段,方法)
- 通过反射,可以修改变量的值可以调用关联的方法
- 使用反射,需要import("reflect")
变量 interface{}和reflect.Value是可以相互转换
func test(b interface{}){
//如何将interface{}转成reflect.Value
rval:=reflect.ValueOf(b)l:=reflect.ValueOf(b)
//如何将reflect.Value ->interface{}
iVal:=rVal.Interface()
//如何将interface{}转成原来的变量类型,使用类型断言
v:=iVal.(Stu)
}
反射的基本类型
func reflectTest01(b interface{}) {
rTyp := reflect.TypeOf(b)
rVal := reflect.ValueOf(b)
fmt.Println(rTyp,rVal) //通过输出的可以看到rVal的值,但是这个值不可以用于运算,因为是relect.Value类型
n := rVal.Int() //通过relect包提供的各种接口可以取到真正的值Int Float String等等
fmt.Println(n+2)
iv :=rVal.Interface() //将rVal转成interface{}
num2 := iv.(int) //将interface{}通过断言转成需要的类型
fmt.Println(num2)
}
对结构体的反射
反射是运行时反射,所以转成接口时在运行时是我们想用的数据,但是如果不断言转换,编译会报错,因为编译时不知道它是什么样的数据
package main
import (
"fmt"
"reflect"
)
type Student struct {
Name string
Age int
}
type Monster struct {
Name string
Age int
}
func reflectTest01(b interface{}) {
rTyp := reflect.TypeOf(b)
rVal := reflect.ValueOf(b)
//获取变量对应的kind的两种方法
//(1) rVal.Kind()
//(2) rTyp.kind()
kind1 := rVal.Kind() //kind= struct
kind2 := rVal.Kind() // kind=struct
fmt.Printf("kind= %v kind=%v\n",kind1,kind2)
fmt.Println(rTyp, rVal) //main.Student {Tom 24} 这里虽然输出是这样,但实际是reflect.Type和reflect.Val类型
fmt.Printf("%T\n", rVal) //reflect.Value
rInter := rVal.Interface()
//运行时反射,下面语句的输出可以看到这里在运行时就是我们的student实例
fmt.Printf("%T", rInter) //main.Studenti am student type
switch rInter.(type) { //可以用switch type来判断是哪种类型
case Student:
fmt.Println("i am student type")
case Monster:
fmt.Println("i am monster type")
}
stu, ok := rInter.(Student) //使用ok模型来防止类型断言出现初五
if ok {
fmt.Println(stu.Name) //Tom
}
}
func main() {
stu := Student{"Tom", 24}
reflectTest01(stu)
}
反射的注意事项和细节
- reflect.Value.Kind获取变量的类别,返回的是一个常量
- Type是类型,kind是类别,Type和Kind可能是相同的,也可能是不同的
- var sum int = 10 num的Type是int,Kind也是int
- var stu Student stu的Type是包名.Student, Kind是struct
通过反射来修改变量
func reflectTest01(b interface{}) {
rVal := reflect.ValueOf(b)
fmt.Printf("rVal kind=%v\n", rVal.Kind())
rVal.Elem().SetInt(20) //Elem返回v持有的接口保管的值的Value封装,或者v持有的指针的值得Value封装
}
func main() {
var num int = 10
reflectTest01(&num)
fmt.Println("num=", num)
}
使用反射来遍历结构体的字段,调用结构体的方法,并获取结构体的值
package main
import "reflect"
import "fmt"
type Monster struct {
Name string `json:"name"`
Age int `json:"age"`
Score int
}
func (s Monster) Print() {
fmt.Println("--start--")
}
func (s Monster) Hello() {
fmt.Println("--Hello--")
}
func (s Monster) GetSum(m, n int) int {
return m + n
}
func TestStruct(a interface{}) {
typ := reflect.TypeOf(a)
val := reflect.ValueOf(a)
kd := val.Kind() //获取到a对应的类型
if kd != reflect.Struct { //如果传入的不是struct,就退出
fmt.Println("expect struct")
return
}
num := val.NumField() //获取到该结构体有几个字段
fmt.Printf("Struct has %d fileds\n", num)
for i := 0; i < num; i++ {
//获取到struct标签,注意需要通过reflect.Type来获取tag标签的值
tagVal := typ.Field(i).Tag.Get("json")
//如果该字段有标签就显示否则不显示,这里的Score没有做转换,所以打印出来没有它
if tagVal != "" {
fmt.Printf("filed %d: tag为%v\n", i, tagVal)
}
}
//获取到结构体有几个方法
numOfMethod := val.NumMethod()
fmt.Printf("Struct has %d methods\n", numOfMethod)
var params []reflect.Value //声明了[]reflect.Value
params = append(params, reflect.ValueOf(10))
params = append(params, reflect.ValueOf(40))
res := val.Method(0).Call(params) //获取第一个方法,反射的排序是根据函数名来排序的。H在P前面所以hello是第一个方法}
fmt.Print(res[0])
}
func main() {
monster := Monster{"牛魔王", 1000, 100}
TestStruct(monster)
}
一个结构体如果实现了n个接口的方法,那么它可以赋值给这n个接口
package main
import "fmt"
type usb interface {
run()
walk()
}
type phone interface {
run()
}
type machine struct {
}
func (c *machine) run() {
fmt.Println("hello")
}
func (c *machine) walk() {
}
func main() {
var u usb
var m *machine
u = m
u.run()
}