Go|Go 基础知识三

方法method
【Go|Go 基础知识三】Go 中虽没有class,但依旧有method
通过显示说明receiver来实现与某个类型的组合
只能为同一个包中的类型定义方法
Receiver 可以是类型的值或者指针
不存在方法重载
可以使用值或指针来调用方法,编译器会自动完成转换
从某种意义上来说,方法是函数的语法糖,因为receiver其实就是
方法所接收的第1个参数(Method Value vs. Method Expression)
如果外部结构和嵌入结构存在同名方法,则优先调用外部结构的方法
类型别名不会拥有底层类型所附带的方法
方法可以调用结构中的非公开字段

package mainimport ( "fmt" )type A struct { Name string } type B struct { Name string }func main() { a := A{} a.Print() fmt.Println(a.Name)b := B{} b.Print() fmt.Println(b.Name) }//绑定A结构,定义Print方法 //* 通过指针来操作Name func (a *A) Print() { a.Name = "AA" fmt.Println("A") }//绑定B结构,定义Print方法func (b B) Print() { b.Name = "BB" fmt.Println("B") }

Go|Go 基础知识三
文章图片
底层结构,如int型也可以添加方法
type TZ intfunc main() { var a TZ a.Pring() //另外一种调用的方法 (*TZ).Pring(&a)}func (a *TZ) Pring() { fmt.Println("TZ") }

Go|Go 基础知识三
文章图片
对同一个package来说,方法的公有和私有都不重要,首字母大写是公有方法,首字母小写是私有方法。
示例
根据为结构增加方法的知识,尝试声明一个底层类型为int的类型,
并实现调用某个方法就递增100。
如:a:=0,调用a.Increase()之后,a从0变成100。
//定义TZ为底层int类型 type TZ intfunc main() { var a TZ a.Increase(100) fmt.Println(a)}//定义Increse方法 func (tz *TZ) Increase(num int) { //相同类型的才能一起运算,num的int类型要强制转换为TZ *tz += TZ(num) }

Go|Go 基础知识三
文章图片
接口interface
接口是一个或多个方法签名的集合
只要某个类型拥有该接口的所有方法签名,即算实现该接口,无需显示
声明实现了哪个接口,这称为 Structural Typing
接口只有方法声明,没有实现,没有数据字段
接口可以匿名嵌入其它接口,或嵌入到结构中
将对象赋值给接口时,会发生拷贝,而接口内部存储的是指向这个
复制品的指针,既无法修改复制品的状态,也无法获取指针
只有当接口存储的类型和对象都为nil时,接口才等于nil
接口调用不会做receiver的自动转换
接口同样支持匿名字段方法
接口也可实现类似OOP中的多态
空接口可以作为任何类型数据的容器
//USB 接口 type USB interface { //Name方法,返回接口的名称 Name() string //Connect方法,用来连接 Connect() }// 1、PhoneConnecter 结构实现 USB接口 type PhoneConnecter struct { name string }// 2、定义PhoneConnecter结构的Name()方法 func (pc PhoneConnecter) Name() string { //返回结构的名称 return pc.name }// 3、定义PhoneConnecter结构的Connect()方法 func (pc PhoneConnecter) Connect() { //输出信息!! fmt.Println("Conneting... OK !", pc.name) }func main() {var a USB //给name字面值初始化 a = PhoneConnecter{"Phone"} a.Connect()}

Go|Go 基础知识三
文章图片
嵌入接口实现
package mainimport ( "fmt" )//USB 接口 type USB interface { //Name方法,返回接口的名称 Name() string //嵌入Connecter方法,用来连接 Connecter }//嵌入Connectertype Connecter interface { Connect() }// 1、PhoneConnecter 结构实现 USB接口 type PhoneConnecter struct { name string }// 2、定义PhoneConnecter结构的Name()方法 func (pc PhoneConnecter) Name() string { //返回结构的名称 return pc.name }// 3、定义PhoneConnecter结构的Connect()方法 func (pc PhoneConnecter) Connect() { //输出信息!! fmt.Println("Conneting... OK !", pc.name) }func main() {// var a USB // 给name字面值初始化 // a = PhoneConnecter{"Phone"}// 通过接收USB类型的参数的Disconnect函数来判断是否实现了 a := PhoneConnecter{"Phone is ok"} a.Connect() Disconnect(a)}func Disconnect(usb USB) {//通过类型判断哪个设备断开了,usb.(判断的类型) if pc, ok := usb.(PhoneConnecter); ok { fmt.Println("Disconnected:", pc.name) return }fmt.Println("Unknow decive.") }

Go|Go 基础知识三
文章图片
空接口,可以实现类拟于其它语言class的继承
package mainimport ( "fmt" )//USB 接口 type USB interface { //Name方法,返回接口的名称 Name() string //嵌入Connecter方法,用来连接 Connecter }//嵌入Connectertype Connecter interface { Connect() }// 1、PhoneConnecter 结构实现 USB接口 type PhoneConnecter struct { name string }// 2、定义PhoneConnecter结构的Name()方法 func (pc PhoneConnecter) Name() string { //返回结构的名称 return pc.name }// 3、定义PhoneConnecter结构的Connect()方法 func (pc PhoneConnecter) Connect() { //输出信息!! fmt.Println("Conneting... OK !", pc.name) }func main() {// var a USB // 给name字面值初始化 // a = PhoneConnecter{"Phone"}// 通过接收USB类型的参数的Disconnect函数来判断是否实现了 a := PhoneConnecter{"Phone is ok"} a.Connect() Disconnect(a)}//interface{}空接口,任何类型都是实现空接口 func Disconnect(usb interface{}) {//通过类型判断哪个设备断开了,usb.(判断的类型)switch v := usb.(type) { case PhoneConnecter: fmt.Println("Disconnected:", v.name) default: fmt.Println("Unknow decive.") }}

Go|Go 基础知识三
文章图片
接口的转换
反射reflection
反射可大大提高程序的灵活性,使得 interface{} 有更大的发挥余地
反射使用 TypeOf 和 ValueOf 函数从接口中获取目标对象信息
反射会将匿名字段作为独立字段(匿名字段本质)
想要利用反射修改对象状态,前提是 interface.data 是 settable,
即 pointer-interface
  • 通过反射可以“动态”调用方法
获取字段的信息,类型的信息,和字段的值
package mainimport ( "fmt" "reflect" )//定义User结构type User struct { Idint Name string Ageint }//定义User结构的方法 func (u User) Hello() { fmt.Println("hello world.") }func main() { u := User{1, "OK", 12} //u是以值拷备的形式传到Info当中 Info(u) }//定义Info函数,参数为空接口转入结构 func Info(o interface{}) { //TypeOf获得接口的类型 t := reflect.TypeOf(o) //打印类型的名称 fmt.Println("Type:", t.Name())//取值 v := reflect.ValueOf(o) fmt.Println("Fields:")for i := 0; i < t.NumField(); i++ { //通过索引取得字段 f := t.Field(i) //取得字段的值 val := v.Field(i).Interface()//按格式,打印 fmt.Printf("%6s:%v = %v\n", f.Name, f.Type, val)}//迭代取得方法的信息 for i := 0; i < t.NumMethod(); i++ { m := t.Method(i) fmt.Printf("%6s:%v\n", m.Name, m.Type) }}

Go|Go 基础知识三
文章图片
反射匿名和嵌入字段 示例
//定义User结构type User struct { Idint Name string Ageint }type Manager struct { User title string }func main() { m := Manager{User: User{1, "OK", 12}, title: "manager"} //取得类型 t := reflect.TypeOf(m)fmt.Printf("%#v\n", t.FieldByIndex([]int{0, 1})) }

Go|Go 基础知识三
文章图片
通过反射来修改值 示例一
package mainimport ( "fmt" "reflect" )func main() { x := 123 //反射出地址 v := reflect.ValueOf(&x) //调用Elem方法,SetInt 改变数值 v.Elem().SetInt(999) fmt.Println(x) }

Go|Go 基础知识三
文章图片
通过反射来修改值 示例二
package mainimport ( "fmt" "reflect" )type User struct { Idint Name string Ageint }func main() { u := User{1, "ok", 12} Set(&u) fmt.Println(u) }func Set(o interface{}) { v := reflect.ValueOf(o) //如果不是指针的话,打印错误,return if v.Kind() == reflect.Ptr && !v.Elem().CanSet() { fmt.Println("xxx") return } else { //取得实际对象 v = v.Elem() }//判断如果找不到Name的话,return f := v.FieldByName("Name") if !f.IsValid() { fmt.Println("BAD") return } //判断如果是Name是reflect.String就修改值 if f.Kind() == reflect.String { f.SetString("BYEBYE") }}

Go|Go 基础知识三
文章图片
通过反射来动态修改方法 示例一
通过反射来动态调用Hello方法
package mainimport ( "fmt" "reflect" )type User struct { Idint Name string Ageint }func (u User) Hello(name string) { //实现User结构Hello方法 fmt.Println("hello", name, ",my name is ", u.Name) }func main() { u := User{1, "ok", 12} //取得发射的User结构 v := reflect.ValueOf(u) //取得反射的User结构的方法Hello mv := v.MethodByName("Hello")//args是reflect.Value类型的Slice args := []reflect.Value{reflect.ValueOf("joe")} //调用Call方法执行 mv.Call(args)}

Go|Go 基础知识三
文章图片
定义一个结构,通过反射来打印其信息,并调用方法
待续。。。。

    推荐阅读