Go中的赋值跟类型转换:
在java中反射是可以获取继承关系,而go语言实际是不支持继承的,所以必须是相同的类型才能使用AssignableTo(),ConvertibleTo()
package main
import (
"fmt"
"reflect"
)
type User struct {
Name string
}
func demo(){
user:=User{
Name:"Hello",
}
u:=reflect.TypeOf(user)
user2:=User{
Name: "world",
}
u1:=reflect.TypeOf(user2)
b:=u.AssignableTo(u1)//u1类型的是否可以赋值给u2类型的---实际就是判断一下 类型1跟类型2是否是同一类型
fmt.Println(b)
b1:=u.ConvertibleTo(u1)//是否可以转换
fmt.Println(b1)
}
func main() {
demo()
}
Go中的伪继承
对比java中 的继承 (extends 关键字),Go中无extends关键字,这意味着Go中并没有对继承的支持。但是,Go使用interface实现的功能叫组合,Go是使用组合来实现的继承,说的更精确一点,是使用组合来代替的继承;
伪继承的实现:
話不多説,先看代码:
结构体的嵌套实现继承
package main
import "fmt"
//相当于java中的父类
type Animal struct {
name string
}
//java中的 子类
type Cat struct {
//我要继承 Animal
Animal
like string
}
//为猫定制的的方法
func (c Cat) Eat() string {
//可以看到我们没有在Cat中声明 name属性,但是这里可以使用Animal声明的name
fmt.Println(c.name + "爱吃" + c.like)
return c.like
}
func (a Animal) Name() string {
fmt.Println(a.name)
return a.name
}
func main() {
a := Animal{
name: "小动物",
}
c := Cat{
Animal: Animal{
name: "小猫猫",
}, like: "小鱼干",
}
fmt.Println(c.Name() + c.Eat() + a.Name())
}
这样Cat就继承了Animal中的属性name;
输出结果:
简单来看这种继承的方式实际就是结构体的嵌套(刚开始学,不知道这样理解是否正确)
接下来我们使用接口的方式来进行封装:
接口的方式实现继承:
package main
import "fmt"
//一个接口
type Animals interface {
//接口中的方法
eat()
}
//一个结构体
type Animal struct {
name string
like string
}
type Dog struct {
Animal
sleepTime int
}
type Cat struct {
Animal
hobby string
}
// Dog也实现了 eat
func (d Dog) eat() {
fmt.Println(d.like)
}
// Cat也实现了 eat
func (c Cat) eat() {
fmt.Println(c.like)
}
//实现接口---即实现接口中的方法eat
func (a Animal) eat() {
fmt.Println(a.name + "爱吃" + a.like)
}
func main() {
c := Cat{
Animal: Animal{
name: "小花猫",
like: "小鱼干",
},
hobby: "睡觉",
}
d := Dog{
Animal: Animal{
name: "大黄狗",
like: "大棒骨",
},
sleepTime: 100,
}
testinter(c)
testinter(d)
}
func testinter(animals Animals){
animals.eat()
}
简单来讲跟结构体的嵌套差不多,只不过这次是实现接口,从代码的实际应用来讲,实现接口是为了实现特定的目的,这个跟java中接口的实现的作用是差不多的功能;
接口的实现判断:
增量代码:
fmt.Println("======================")
//获取接口的类型
//先传 nil 进来 强转为接口的指针,再来获得 其类型
animals := reflect.TypeOf((*Animals)(nil)).Elem()
//判断是否实现接口
bools:=cat.Implements(animals)
fmt.Println(bools)
implements 方法要求传入Type类型
不能直接传入接口,
这里通过 将nil强制转换为 接口指针,在然后判断其类型
于是reflect.TypeOf((*Animals)(nil))
就会看起来很怪;
增量代码:
c:=Cat{
Animal: Animal{},
hobby: "小鱼干",
}
cc:=reflect.ValueOf(c)
fmt.Println(cc.IsNil())//判断 cc 是否是 nil
// IsNil reports whether its argument v is nil. The argument must be
// a chan, func, interface, map, pointer, or slice value; if it is //
not, IsNil panics. Note that IsNil is not always equivalent to a //
regular comparison with nil in Go. For example, if v was created // by
calling ValueOf with an uninitialized interface variable i, // i==nil
will be true but v.IsNil will panic as v will be the zero // Value.
源码解释告诉我们 只有是chan, func, interface, map, pointer, or slice ,否则就会报错 ,所以在使用之前要判断一下是否符合调用的条件在做处理;
当同时调用 cc.IsNil() ,cc.isValid()时会出现错误,导致程序不能正常运行—>>
panic: reflect: call of reflect.Value.IsNil on struct Value
//不能对结构的值调用reflect.Value.IsNil
goroutine 1 [running]:
reflect.Value.IsNil(...)
注销掉其中一个—>>
运行结果如下:
值转为Type
func main() {
//返回值为Value类型
i:=reflect.ValueOf(100)
g:=reflect.ValueOf("abc")
//打印的是值
fmt.Println(i)
fmt.Println(g)
//可以直接强转--如果确定数据类型的话
fmt.Println(i.Int())
fmt.Println(g.String())
//将Value转为Type
h:=i.Type()
k:=g.Type()
//判断类型
fmt.Println(h.Kind()==reflect.Int)
fmt.Println(k.Kind()==reflect.String)
}
Golang中的reflect.Elem()函数用于获取接口v包含的值或指针v指向的值
调用该函数Elem()只能是接口指针
反射修改数据
通过反射可以修改原始变量的值,但是需要一定的条件支持,传入的实参必须是指针类型
package main
import (
"fmt"
"reflect"
)
func main(){
//字符串
var s ="hello"
//这里要传指针,否则不会修改
vs:=reflect.ValueOf(&s)
vs.Elem().SetString("world")
fmt.Println(s)
}
这里通过 Elem()方法把指针Value转换为非指针Value----即通过反射获取指针指向的元素类型,这个获取过程被称为取元素,等效于对指针类型变量做了一个*操作
我们来操作一下struct
package main
import (
"fmt"
"reflect"
)
type user struct {
name string
age int
}
func main(){
//字符串
var s ="hello"
//这里要传指针,否则不会修改
vs:=reflect.ValueOf(&s)
vs.Elem().SetString("world")
fmt.Println(s)
u:=user{
name: "小红",
age: 18,
}
h:=reflect.ValueOf(&u)//传指针
k:=h.Elem().FieldByName("name")//根据属性名获取值 Elem将其转为值
fmt.Println(k)
fmt.Println(k.CanSet())//只有值是可寻址的时候才可以被修改---即可以被外界访问的
if k.CanSet(){
k.SetString("小明")
//k.Elem().SetString("小明")
fmt.Println(u.name)
}
}
输出结果:
因为这里name属性的访问权限问题,所以 canSet()的结果为false, 我们将user属性name改为"Name" 再来看输出结果—>>
当 我们通过反射去修改结构体中的属性时,这个属性 要是能够被访问的—即首字母大写,
如果是接口中的即使时小写的也可以被访问;
反射构建对象
反射创建struct
package main
import (
"fmt"
"reflect"
)
type User struct {
Name string
age int
}
//声明一个切片
var slice1 []User
func main() {
//Users 切片实例化
user1 := make([]User, 10)
//向切片添加元素
user1 = append(user1, User{
Name: "小红",
age: 20,
})
user1 = append(user1, User{
Name: "小明",
age: 20,
})
u := reflect.ValueOf(&user1)
fmt.Println(u)
test()
}
func test(){
uu:=User{
Name: "test",
age: 0,
}
//获取了类型
userType:=reflect.TypeOf(uu)
//反射去构造一个对象,此时返回的是一个指针
userValue:=reflect.New(userType)
//获得其中的属性
userValue.Elem().FieldByName("Name").SetString("小花")
//将value转化为user类型,这里传的应该是一个值,而不是指针
if u ,ok:= userValue.Interface().(*User) ;ok{
fmt.Println(u.Name)
}
}
运行结果:
反射创建切片
//声明一个切片
var slice1 []User
//获取切片的类型
sliceType := reflect.TypeOf(slice1)
//构建一个slice 这里是一个指针
sliceValue := reflect.MakeSlice(sliceType, 2, 8)
//sliceValue.SetLen(2)
//
sliceValue.Index(0).Set(reflect.ValueOf(User{
Name: "创建的slice1",
age: 10,
}))
sliceValue.Index(1).Set(reflect.ValueOf(User{
Name: "创建的slice2",
age: 10,
}))
if uu,ok:=sliceValue.Interface().([]User);ok{
fmt.Println(uu[0].Name)
fmt.Println(uu[1].Name)
}
运行结果---->>
一些异常的展示:
这里是为了在以后工作时找到报错的一些情况:
package main
import (
"fmt"
"reflect"
)
type User struct {
Name string
Age int
}
func demo() {
uu:=User{
Name: "一个名字而已",
Age: 100,
}
//获得一个类型
UserType:=reflect.TypeOf(uu)
//去创建一个对象,这里是指针指向这个对象
newUser:=reflect.New(UserType)
//将这个对象的属性赋值
newUser.Elem().FieldByName("Name").SetString("换个名字又何妨")
newUser.Elem().FieldByName("age").SetInt(111)//注意这里获得的属性 age 在User中并不存在
//将这个对象转为实际的对象而非指针指向这个对象
//断言他为User--这里要传值*User
if uu,ok:=newUser.Interface().(*User);ok{
//打印这个对象的名字吧
fmt.Println(uu.Name)
fmt.Println(uu.Age)
}
}
func main() {
demo()
}
这时候会报panic
panic: reflect: call of reflect.Value.SetInt on zero Value
goroutine 1 [running]:
reflect.flag.mustBeAssignableSlow(0x0?)
这里报错—>>是因为设置 int的时候 找不到属性—即不能将不存在的value设置值,所以在设置时要判断是否存在该属性;但是我们在看源码时发现当在调用这个方法的值无效时会直接报错----即值无效是无法调用这个方法的;
为了不报panic,所以可以使用IsValid() 来做判断;
n:=newUser.Elem().FieldByName("age")
if n.IsValid() {
n.SetInt(111)
}
另一个panic就是当User struct的属性为小写字母时—即不可被导出,我们在调用FieldByName()时会报错
panic: reflect: reflect.Value.SetString using value obtained using unexported field
在使用go的反射机制时要注意的一些事情是:
1,结构体的属性是否可被导出
2,通过反射获得的Value或者Type isValid ,isZero.isNil 是否为true
func main() {
demo()
var s []User
//获得切片类型
sliceType:=reflect.TypeOf(s)
//构建一个slice
sliceT:=reflect.MakeSlice(sliceType,2,8)
sliceT.Index(0).Set(reflect.ValueOf(User{
Name: "第一个用户",
Age: 10,
}))
sliceT.Index(1).Set(reflect.ValueOf(User{
Name: "第2个用户",
Age: 122,
}))
uu:=sliceT.Interface().([]User)
for i := 0; i < len(uu); i++ {
fmt.Println(uu[i].Name)
}
}
下面代码报错了,
panic: interface conversion: interface {} is *[]main.User, not []main.User
//修改切片的长度
sliceV:=reflect.ValueOf(&S2)
sliceV.Elem().SetLen(19)
sliceV.Elem().Index(0).Set(reflect.ValueOf(User{
Name: "我就不信了",
Age: 100,
}))
ss:=sliceV.Interface().([]User)
fmt.Println(len(ss))
不报错的代码如下,
运行结果:
对比发现原来ss:=这里要通过Elem()转为实际的类型;
小结一下:
如有理解不到位的地方还请老师指点
在通过反射创建 struct或者其他引用类型的对象时, 通过reflect.Typeof()获得其类型
然后通过reflect.Valueof()获得其 带有指针的Value
通过Elem ()来将其转为非指针的,这样才能修改其中的属性;
反射调用方法
//通过反射调用方法
func main() {
//反射获得方法
funcValue:=reflect.ValueOf(TestMap)
//调用方法--->>参数 数组的形式 也要转换为反射的Value
result:=funcValue.Call([]reflect.Value{reflect.ValueOf(3),reflect.ValueOf(5)})
//因为只有一个返回值,所以取0就行了
result2:=result[0].Interface().(int)
fmt.Println(result2)
}
结果:
这里反射调用时的参数要是value ---->>
源码:
func (v Value) Call(in []Value) []Value { v.mustBe(Func) //必须是一个方法 v.mustBeExported()//方法必须可被外界访问--首字母大写 return v.call("Call", in) }
源码要求我们传入的是[]Value(通过reflect.Value()转为 Value),返回值也是[]Value(因为返回值可能不止一个,所以用[]Value来接收)