运维八一 运维八一
首页
运维杂记
编程浅尝
周积跬步
专栏
生活
关于
收藏
  • 分类
  • 标签
  • 归档
Source (opens new window)

运维八一

运维,运维!
首页
运维杂记
编程浅尝
周积跬步
专栏
生活
关于
收藏
  • 分类
  • 标签
  • 归档
Source (opens new window)
  • Go

    • 前言

    • Go基础知识

    • Go基本语法

    • 实战项目:简单web服务

    • 基本数据类型

    • 内置运算符

    • 分支和循环

    • 函数 function

    • 结构体 struct

    • 方法 method

      • 认识方法
        • 1. 认识方法
          • 1.1 方法与函数的异同
          • 1.2 receiver 参数
      • 方法的本质
      • 方法的设计
      • 用类型嵌入模拟实现“继承”
    • 实战项目:跟踪函数调用链

    • 接口 interface

    • 并发 concurrency

    • 指针

    • 实战项目:实现轻量级线程池

    • 实战项目:实现TCP服务器

    • go常用包

    • Gin框架

    • go随记

  • Python

  • Shell

  • Java

  • Vue

  • 前端

  • 编程浅尝
  • Go
  • 方法 method
lyndon
2022-06-07
目录

认识方法

# 1. 认识方法

以 Go 标准库 net/http 包中 *Server 类型的方法 ListenAndServeTLS 为例,讲解一下 Go 方法的一般形式:

img

# 1.1 方法与函数的异同

  • 相同点:

    • Go 的方法是以 func 关键字修饰的,和函数一样,包含方法名(对应函数名)、参数列表、返回值列表与方法体(对应函数体);

    • 方法中的这几个部分和函数声明中对应的部分,在形式与语义方面都是一致的,比如:方法名字首字母大小写决定该方法是否是导出方法;方法参数列表支持变长参数;方法的返回值列表也支持具名返回值等。

  • 不同点:

    • 和由五个部分组成的函数声明不同,Go 方法的声明有六个组成部分,多的一个就是图中的 receiver 部分。在 receiver 部分声明的参数,Go 称之为 receiver 参数,这个 receiver 参数也是方法与类型之间的纽带,也是方法与函数的最大不同。

# 1.2 receiver 参数

Go 中的方法必须是归属于一个类型的,而 receiver 参数的类型就是这个方法归属的类型,或者说这个方法就是这个类型的一个方法。

以上图中的 ListenAndServeTLS 为例,这里的 receiver 参数 srv 的类型为 *Server,那么就可以说,这个方法就是 *Server 类型的方法。

将上面例子中的方法声明,转换为一个方法的一般声明形式:

func (t *T或T) MethodName(参数列表) (返回值列表) {
    // 方法体
}
1
2
3

无论 receiver 参数的类型为 *T 还是 T,都把一般声明形式中的 T 叫做 receiver 参数 t 的基类型。

如果 t 的类型为 T,那么说这个方法是类型 T 的一个方法;如果 t 的类型为 *T,那么就说这个方法是类型 *T 的一个方法。

而且,要注意的是,每个方法只能有一个 receiver 参数,Go 不支持在方法的 receiver 部分放置包含多个 receiver 参数的参数列表,或者变长 receiver 参数。

**receiver 参数作用域:**方法接收器(receiver)参数、函数 / 方法参数,以及返回值变量对应的作用域范围,都是==函数 / 方法体对应的显式代码块==。

**receiver 部分的参数名具有唯一性:**不能与方法参数列表中的形参名,以及具名返回值中的变量名存在冲突,必须在这个方法的作用域中具有唯一性。

如果这个不唯一不存在,Go 编译器就会报错:

type T struct{}

func (t T) M(t string) { // 编译器报错:duplicate argument t (重复声明参数t)
    ... ...
}
1
2
3
4
5

如果在方法体中,没有用到 receiver 参数,也可以省略 receiver 的参数名(这一情况很少使用,了解一下就好):

type T struct{}

func (T) M(t string) { 
    ... ...
}
1
2
3
4
5

**receiver 参数的基类型约束:**receiver 参数的基类型本身不能为指针类型或接口类型。

基类型为指针类型和接口类型时,Go 编译器会报错:

type MyInt *int
func (r MyInt) String() string { // r的基类型为MyInt,编译器报错:invalid receiver type MyInt (MyInt is a pointer type)
    return fmt.Sprintf("%d", *(*int)(r))
}

type MyReader io.Reader
func (r MyReader) Read(p []byte) (int, error) { // r的基类型为MyReader,编译器报错:invalid receiver type MyReader (MyReader is an interface type)
    return r.Read(p)
}
1
2
3
4
5
6
7
8
9

Go 对方法声明的位置也是有约束的,Go 要求,方法声明要与 receiver 参数的基类型声明放在同一个包内。

  • 不能为原生类型(诸如 int、float64、map 等)添加方法;

    // 为 Go 原生类型 int 增加新方法 Foo
    func (i int) Foo() string { // 编译器报错:cannot define new methods on non-local type int
        return fmt.Sprintf("%d", i) 
    }
    
    1
    2
    3
    4
  • 不能跨越 Go 包为其他包的类型声明新方法。

    // 跨越包边界,为 Go 标准库中的 http.Server 类型添加新方法 Foo
    import "net/http"
    
    func (s http.Server) Foo() { // 编译器报错:cannot define new methods on non-local type http.Server
    }
    
    1
    2
    3
    4
    5

和其他主流编程语言相比,Go 语言的方法,只比函数多出了一个 receiver 参数,这就大大降低了 Gopher 们学习方法这一语法元素的门槛。

type T struct{}

func (t T) M(n int) {
}

func main() {
    var t T
    t.M(1) // 通过类型T的变量实例调用方法M

    p := &T{}
    p.M(2) // 通过类型*T的变量实例调用方法M

}
1
2
3
4
5
6
7
8
9
10
11
12
13
上次更新: 2022/06/12, 15:48:09
encoding-json包
方法的本质

← encoding-json包 方法的本质→

最近更新
01
ctr和crictl显示镜像不一致
03-13
02
alpine镜像集成常用数据库客户端
03-13
03
create-cluster
02-26
更多文章>
Theme by Vdoing | Copyright © 2015-2024 op81.com
苏ICP备18041258号-2
  • 跟随系统
  • 浅色模式
  • 深色模式
  • 阅读模式