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" 再来看输出结果—>>
当 我们通过反射去修改结构体中的属性时,这个属性 要是能够被访问的—即首字母大写,
如果是接口中的即使时小写的也可以被访问;