方法method
Go 中虽没有class,但依旧有method
通过显示说明receiver来实现与某个类型的组合
只能为同一个包中的类型定义方法
Receiver 可以是类型的值或者指针
不存在方法重载
可以使用值或指针来调用方法,编译器会自动完成转换
从某种意义上来说,方法是函数的语法糖,因为receiver其实就是
方法所接收的第1个参数(Method Value vs. Method Expression)
如果外部结构和嵌入结构存在同名方法,则优先调用外部结构的方法
类型别名不会拥有底层类型所附带的方法
方法可以调用结构中的非公开字段
package main
import (
"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")
}
底层结构,如int型也可以添加方法
type TZ int
func main() {
var a TZ
a.Pring()
//另外一种调用的方法
(*TZ).Pring(&a)
}
func (a *TZ) Pring() {
fmt.Println("TZ")
}
对同一个package来说,方法的公有和私有都不重要,首字母大写是公有方法,首字母小写是私有方法。
示例
根据为结构增加方法的知识,尝试声明一个底层类型为int的类型,
并实现调用某个方法就递增100。
如:a:=0,调用a.Increase()之后,a从0变成100。
//定义TZ为底层int类型
type TZ int
func main() {
var a TZ
a.Increase(100)
fmt.Println(a)
}
//定义Increse方法
func (tz *TZ) Increase(num int) {
//相同类型的才能一起运算,num的int类型要强制转换为TZ
*tz += TZ(num)
}
接口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()
}
嵌入接口实现
package main
import (
"fmt"
)
//USB 接口
type USB interface {
//Name方法,返回接口的名称
Name() string
//嵌入Connecter方法,用来连接
Connecter
}
//嵌入Connecter
type 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.")
}
空接口,可以实现类拟于其它语言class的继承
package main
import (
"fmt"
)
//USB 接口
type USB interface {
//Name方法,返回接口的名称
Name() string
//嵌入Connecter方法,用来连接
Connecter
}
//嵌入Connecter
type 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.")
}
}
接口的转换
反射reflection
反射可大大提高程序的灵活性,使得 interface{} 有更大的发挥余地
反射使用 TypeOf 和 ValueOf 函数从接口中获取目标对象信息
反射会将匿名字段作为独立字段(匿名字段本质)
想要利用反射修改对象状态,前提是 interface.data 是 settable,
即 pointer-interface
- 通过反射可以“动态”调用方法
获取字段的信息,类型的信息,和字段的值
package main
import (
"fmt"
"reflect"
)
//定义User结构
type User struct {
Id int
Name string
Age int
}
//定义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)
}
}
反射匿名和嵌入字段 示例
//定义User结构
type User struct {
Id int
Name string
Age int
}
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}))
}
通过反射来修改值 示例一
package main
import (
"fmt"
"reflect"
)
func main() {
x := 123
//反射出地址
v := reflect.ValueOf(&x)
//调用Elem方法,SetInt 改变数值
v.Elem().SetInt(999)
fmt.Println(x)
}
通过反射来修改值 示例二
package main
import (
"fmt"
"reflect"
)
type User struct {
Id int
Name string
Age int
}
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")
}
}
通过反射来动态修改方法 示例一
通过反射来动态调用Hello方法
package main
import (
"fmt"
"reflect"
)
type User struct {
Id int
Name string
Age int
}
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)
}
定义一个结构,通过反射来打印其信息,并调用方法
待续。。。。