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

}

反射匿名和嵌入字段 示例

//定义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)

}
定义一个结构,通过反射来打印其信息,并调用方法
待续。。。。
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 158,425评论 4 361
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 67,058评论 1 291
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 108,186评论 0 243
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 43,848评论 0 204
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,249评论 3 286
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,554评论 1 216
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,830评论 2 312
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,536评论 0 197
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,239评论 1 241
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,505评论 2 244
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 32,004评论 1 258
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,346评论 2 253
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 32,999评论 3 235
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,060评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,821评论 0 194
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,574评论 2 271
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,480评论 2 267

推荐阅读更多精彩内容