Go 入门 - 方法和接口

本文深入探讨了Go语言中的方法和接口概念,包括方法的接受者、如何为结构体定义方法、指针接受者的作用,以及接口的定义和使用。通过实例展示了如何通过接口实现多态性,介绍了类型断言和类型选择的使用方法。

方法和接口

方法的接受者

Go中没有类,取而代之的是在结构体上定义的方法

为了将方法(函数)绑定在某一类结构体上,我们在定义函数(方法)时引入“接受者”的概念。

方法接受者在它自己的参数列表内,位于func关键字和方法名之间

package main

import (
    "fmt"
    "math"
)

type Vertex struct {
    X, Y float64
}

func (v Vertex) Abs() float64 { //说明该方法绑定给特殊的函数
    return math.Sqrt(v.X*v.X + v.Y*v.Y)
}

func Abs(v Vertex) float64 { //正常的函数也可以这样写,并且不会和上一个函数重载
    return math.Sqrt(v.X*v.X + v.Y*v.Y)
}

func main() {
    v := Vertex{3, 4}
    fmt.Println(v.Abs()) // output 5
    fmt.Println(Abs(v)) // output 5
}

注意,接受者只能是同一包内定义的类型,不能使其他包内定义的类型或者内建类型。如果想要为float64声明一个接受者,那也只能重新定义一个类型type MyFloat float64

指针接受者

接受者可以是指向某类结构体的指针。如果是指针,那么在就可以修改结构体内部的变量。

当指针为接受者的方法被调用时,接受者既能为值也能为指针。同样,当以值为接受者的方法被调用的时候,接受者技能为值又能为指针。在这个一一下,接受者是值还是指针本质上区别是方法是否可以修改结构体的变量。

接口

接口定义了一个方法的集合。不同的结构体通过至少实现这些方法可以生成不同类型的接口。接口值可以看做包含值和具体类型的元组。

package main

import "fmt"

type I interface {
    M()
}

type T struct {
    S string
}

func (t *T) M() {
    if t == nil {
        fmt.Println("<nil>")
        return
    }
    fmt.Println(t.S)
}

func main() {
    var i I

    var t *T
    i = t // 类型赋予接口,需要注意的是 nil值的接口的方法也会被调用
    describe(i)
    i.M()

    i = &T{"hello"}
    describe(i)
    i.M()
}

func describe(i I) {
    fmt.Printf("(%v, %T)\n", i, i)
}

指定了零个方法的接口被称为空接口interface{}是空接口的类型,写在函数的参数表里面也是这样写的

package main

import "fmt"

func main() {
    var i interface{}
    describe(i)

    i = 42
    describe(i)

    i = "hello"
    describe(i)
}

func describe(i interface{}) {
    fmt.Printf("(%v, %T)\n", i, i)
}

类型断言

类型断言 提供了访问接口值底层具体值的方式。

t := i.(T)

该语句断言接口值 i 保存了具体类型 T,并将其底层类型为 T 的值赋予变量 t

i 并未保存 T 类型的值,该语句就会触发一个恐慌。

为了 判断 一个接口值是否保存了一个特定的类型,类型断言可返回两个值:其底层值以及一个报告断言是否成功的布尔值。

t, ok := i.(T)

i 保存了一个 T,那么 t 将会是其底层值,而 oktrue

否则,ok 将为 falset 将为 T 类型的零值,程序并不会产生恐慌。

请注意这种语法和读取一个映射时的相同之处。

package main

import "fmt"

func main() {
    var i interface{} = "hello"

    s := i.(string)
    fmt.Println(s)

    s, ok := i.(string)
    fmt.Println(s, ok)

    f, ok := i.(float64)
    fmt.Println(f, ok)

    f = i.(float64) // panic
    fmt.Println(f)
}

类型选择

类型选择 是一种按顺序从几个类型断言中选择分支的结构。

类型选择与一般的 switch 语句相似,不过类型选择中的 case 为类型(而非值), 它们针对给定接口值所存储的值的类型进行比较。

switch v := i.(type) {
case T:
    // v 的类型为 T
case S:
    // v 的类型为 S
default:
    // 没有匹配,v 与 i 的类型相同
}

类型选择中的声明与类型断言 i.(T) 的语法相同,只是具体类型 T 被替换成了关键字 type

此选择语句判断接口值 i 保存的值类型是 T 还是 S。在 TS 的情况下,变量 v 会分别按 TS 类型保存 i 拥有的值。在默认(即没有匹配)的情况下,变量 vi 的接口类型和值相同。

package main

import "fmt"

func do(i interface{}) {
    switch v := i.(type) {
    case int:
        fmt.Printf("Twice %v is %v\n", v, v*2)
    case string:
        fmt.Printf("%q is %v bytes long\n", v, len(v))
    default:
        fmt.Printf("I don't know about type %T!\n", v)
    }
}

func main() {
    do(21)
    do("hello")
    do(true)
}

转载于:https://www.cnblogs.com/computing-n-design/p/9927465.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值