Go 语言中的方法(第一部分)

themoonbear · 2018-08-08 22:47:23 · 3685 次点击 · 预计阅读时间 4 分钟 · 大约8小时之前 开始浏览    
这是一个创建于 2018-08-08 22:47:23 的文章,其中的信息可能已经有所发展或是发生改变。

Golang 程序中定义的类型有与其相关的方法。让我们来看一个例子:

type T struct {
    name string
}
func (t T) PrintName() {
    fmt.Println(t.name)
}
func main() {
    t := T{name: "Michał"}
    t.PrintName()
}

正如您可能怀疑程序输出名字。方法是一个函数,拥有附加的,单独元素的参数列表,称之为接收器。它被放在一个方法名之前。接收器的类型决定了如何使用方法。

接收器

方法被绑定到接收器的基类。小例子解释的很好:

type T struct {
    name string
}
func (T) F() {}
func (*T) F() {}

上面的代码无法编译. 第一个 F 方法被绑到 T 上. 第二各方法被邦到 *T 上. 对于单个基类型,方法名必须唯一,所以编译将报错:

> go install github.com/mlowicki/lab && ./spec/bin/lab
# github.com/mlowicki/lab
spec/src/github.com/mlowicki/lab/lab.go:103: method redeclared: T.F
        method(T) func()
        method(*T) func()

如果基类是一个 struct 类型,那么字段名不能和方法名重复。

type T struct {
    M int
}
func (t T) M() {}

编译失败信息如下:

type T has both field and method named M

Go 的类型系统限制了哪些类型可以作为接收器. 它不能是接口类型或指针,因此它不可能为一个空接口(interface{})定义方法来满足所有类型。 只允许使用类型名称,所以,类型字面值会导致编译错误:

func (v map[string]float64) M() {}

错误信息 invalid receiver type map[string]float64 (map[string]float64 is an unnamed type).

方法集

调用一个 T 类型的变量上的 m 方法, 方法 m 必须在与 T 关联的方法集上. 方法集中的一个成员是什么意思?

接口类型

接口类型的方法集是接口自己 — 为实现接口需要定义方法列表:

type I interface {
    F()
    G()
}
type T struct{}
func (T) F() {}
func (T) G() {}
func (T) H() {}
func main() {
    var i I = T{}
    i.F()
    i.G()
    i.H() // error: i.H undefined (type I has no field or method H)
}

上面的例子中,不允许调用 T 类型的变量 i 上的方法。仅有来自接口的接口类型值方法属于接口类型的方法集。(方法集是静态的,当分配不同类型的值时不会改变)。 要调用 H 方法,需要先使用类型断言:

t, ok := i.(T)
if !ok {
    log.Fatal("Type assertion failed")
}
t.H()

非接口类型

对于非接口类型 T,方法集由接收器为 T 的方法组成:

type T struct{}
func (T) F() {}
func (T) G() {}
type U struct{}
func (U) H() {}
func main() {
    t := T{}
    t.F()
    t.G()
    t.H() // error: t.H undefined (type T has no field or method H)
}

当处理 T 类型的指针时, 它的方法集由接收器为 T 或 *T 的方法组成:

type T struct{}
func (T) F() {}
func (T) G() {}
func (*T) H() {}
func main() {
    t := &T{}
    t.F()
    t.G()
    t.H()
}

为什么 T 的方法集没有 T 类型接收器的方法,而 T 的方法集却包含 T 类型接收器的方法呢? 令人惊讶的是,分配给 t 不是地址,但无指针结构值仍执行的很好:

t := T{}
t.F()
t.G()
t.H()

Go 规范描述了明确的情况:

如果 x 是可寻址的,并且 x 的方法集包含 m ,x.M() 是 (&x).M() 的速记。

何时使用值还是指针接收器的建议放在了官方的 FAQ中。

唯一性

类型 T 定义的方法集不能有俩个名字一样的方法。 有相同的方法名但不同类型的参数,在 Go 中是不可能的。 ( Go 中没有 ad hoc polymorphism )。

类型 T 定义的方法集由 T 实现.

打印方法集

Go 有 reflect 包,对于看类型的方法集很有用:

func PrintMethodSet(val interface{}) {
    t := reflect.TypeOf(val)
    fmt.Printf("Number of methods: %d\n", t.NumMethod())
    for i := 0; i < t.NumMethod(); i++ {
        m := t.Method(i)
        fmt.Printf("Method %s\n", m)
        fmt.Printf("\tName: %s\n", m.Name)
        fmt.Printf("\tPackage path: %s\n", m.PkgPath)
    }
}

Method 返回值类型,方法是 Method 类型.

值得一提的是,文档有个 bug, 因为 NumMethod 从方法集返回了方法的数量,但只有那些被导出的方法 (名字以大写字母开头)。 归档在 #17686.

引入类型方法

方法只能被定义在有类型创建的包里。

github.com/mlowicki/lib/lib.go:

package lib
type T struct{}

github.com/mlowicki/lab/lab.go:

package main
import (
    "fmt"
    . "github.com/mlowicki/lib"
)
func (T) F()  {}
func (*T) F() {}
[...]

这会引起构建错误 cannot define new methods on non-local type lib.T.

不使用参数

函数/方法定义不强制命名所有参数或接收者。如果不使用它们,则只能指定类型。 Name can be eventually introduced later on when it’ll actually needed:

type T struct {
    name string
}
func (t T) F(name string) {}
func (T) G(string) {}

G 的声明省略无用标识符。


via: https://medium.com/golangspec/methods-in-go-part-i-a4e575dff860

作者:Michał Łowicki  译者:themoonbear  校对:polaris1119

本文由 GCTT 原创编译,Go语言中文网 荣誉推出


有疑问加站长微信联系(非本文作者))

入群交流(和以上内容无关):加入Go大咖交流群,或添加微信:liuxiaoyan-s 备注:入群;或加QQ群:692541889

3685 次点击  
加入收藏 微博
被以下专栏收入,发现更多相似内容
1 回复  |  直到 2021-05-25 16:31:57
TreeFalling
TreeFalling · #1 · 4年之前

这翻译水准我觉得不行,还是建议直接看原文

添加一条新回复 (您需要 登录 后才能回复 没有账号 ?)
  • 请尽量让自己的回复能够对别人有帮助
  • 支持 Markdown 格式, **粗体**、~~删除线~~、`单行代码`
  • 支持 @ 本站用户;支持表情(输入 : 提示),见 Emoji cheat sheet
  • 图片支持拖拽、截图粘贴等方式上传