Go 基础知识三

方法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)
    }

}
Go 基础知识三_第1张图片

反射匿名和嵌入字段 示例

//定义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}))
}
Go 基础知识三_第2张图片

通过反射来修改值 示例一

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)

}
定义一个结构,通过反射来打印其信息,并调用方法
待续。。。。

你可能感兴趣的:(Go 基础知识三)