写点什么

Golang 基础笔记九之方法与接口

作者:Hunter熊
  • 2025-07-07
    北京
  • 本文字数:3083 字

    阅读完需:约 10 分钟

Golang基础笔记九之方法与接口

本文首发于公众号:Hunter 后端

原文链接:Golang基础笔记九之方法与接口


本篇笔记介绍 Golang 里方法和接口,以下是本篇笔记目录:


  1. 方法

  2. 接口

  3. 用结构体实现类的功能

1、方法

首先介绍一下方法。


方法是与特定类型关联的函数,我们在实现一个函数前,绑定一个类型,就实现了这个类型的方法。


比如我们想实现一个结构体的方法,可以如下操作:


type Person struct {    Name string    Age  int}func (person Person) fmtPersonInfo() {    fmt.Printf("person name is %s, age is %d\n", person.Name, person.Age)}
复制代码


在上面的操作中,我们就为 Person 这个结构体绑定了一个方法,而其调用也很简单,就是实例化一个 Person 结构体后,就可以对其进行调用:


person := Person{Name: "Hunter", Age: 28}person.fmtPersonInfo()
复制代码

方法支持的类型

方法支持绑定的类型有结构体、指针类型、接口类型以及自定义类型,但是不支持绑定 Golang 内置的类型包括 int、slice、map 等。

方法绑定到指针类型

前面介绍了方法绑定到结构体上,这里再介绍一个绑定到指针类型上,还是前面的 Person 结构体,绑定到其指针上,来实现更改 Age 字段的操作:


func (person *Person) ChangeAge(age int) {    person.Age = age}person := Person{Name: "Hunter", Age: 28}person.fmtPersonInfo()person.ChangeAge(18)person.fmtPersonInfo()
复制代码


第二次打印信息就可以看到 person 的 age 已经发生了变化。


这里需要注意一点,Person 结构体是值类型,如果绑定的是其结构体本身,而非其指针类型,在方法中对其更改后,并不会影响结构体本身,比如下面的操作:


func (person Person) NewChangeAge(age int) {    person.Age = age}person := Person{Name: "Hunter", Age: 28}person.fmtPersonInfo()person.NewChangeAge(18)person.fmtPersonInfo()
复制代码


可以看到,这里调用 NewChangeAge() 方法后,没有对 person 这个结构体本身进行更改。

方法绑定到自定义类型

而如果想要绑定 int、slice、map 等内置类型,可以通过方法支持的自定义类型来绑定。


比如我们想要实现使用 slice 绑定一个打印其长度的方法,可以通过自定义类型设置一个别名,通过别名来绑定一个方法:


type MySlce []intfunc (mySlice MySlce) printSliceLength() {    fmt.Printf("mySliceLength is %d\n", len(mySlice))}slice := MySlce{1, 2, 3}slice.printSliceLength()
复制代码

2、接口

1. 接口的定义和实现

接口是一组方法签名的集合,任何类型只要实现了接口中的所有方法,就被认为实现了该接口。


比如下面我们定义了一个形状的接口,内部有面积和周长两个空方法:


type Shape interface {    Area() float64    Perimeter() float64}
复制代码


这样我们就定义了一个接口。


而如果我们要实现这个接口,只需要实现这个接口里的两个方法 Area() 和 Perimeter() 就是实现了这个接口,这个过程是隐式的,不需要显式声明或者绑定。


接下来我们定义 Rectangle 和 Circle 两个结构体,并且实现 Area() 和 Perimeter() 两个方法:


type Rectangle struct {    Width, Height float64}func (r Rectangle) Area() float64 {    return r.Width * r.Height}func (r Rectangle) Perimeter() float64 {    return 2 * (r.Width + r.Height)}type Circle struct {    radius float64}func (c Circle) Area() float64 {    return 3.14 * c.radius * c.radius}func (c Circle) Perimeter() float64 {    return 2 * 3.14 * c.radius}
复制代码


我们已经分别用 Rectangle 和 Circle 这两个结构体实现了 Shape 接口。


那么这个接口在这里有什么作用呢,我们可以实现一个函数,接收接口类型的参数,那么实现了这个接口的结构体都可以作为传入:


func PrintShapeInfo(s Shape) {    fmt.Println("Area: ", s.Area())    fmt.Println("Perimeter: ", s.Perimeter())}func main() {    r := Rectangle{Width: 2, Height: 3}    PrintShapeInfo(r)    c := Circle{Radius: 5}    PrintShapeInfo(c)}
复制代码

2. 类型断言

类型断言用于检查接口值的底层具体类型,并提取该类型的值,其用法示例如下:


value, ok := interfaceValue.(ConcreteType)
复制代码


  1. value 是转换后的具体类型值

  2. ok 是一个布尔值,表示是否断言成功

  3. interfaceValue 是接口类型的变量

  4. ConcreteType 是目标具体类型。比如我们可以修改 PrintShapeInfo 函数,在内部对其进行类型断言:


func PrintShapeInfo(s Shape) {    circle, ok := s.(Circle)    if ok {        fmt.Println("ths shape is circle, the area is: ", circle.Area())    } else {        fmt.Println("this shape is not circle")    }    fmt.Println("Area: ", s.Area())    fmt.Println("Perimeter: ", s.Perimeter())}
复制代码

3. 空接口

空接口(interface{}) 可以表示任何类型的值,常用于处理不确定类型的数据。


比如我们想打印一个输入的变量,但是这个变量的类型不确定,我们可以使用空接口来处理这种情况。


func PrintType(a interface{}) {    switch v := a.(type) {    case int:        fmt.Println("this is int: ", v)    case float64:        fmt.Println("this is float64: ", v)    case string:        fmt.Println("this is string: ", v)    default:        fmt.Println("this is other type: ", v)    }}
func main() { PrintType(1) PrintType(3.4) PrintType("abc")}
复制代码

3、用结构体实现类的功能

在 Golang 里没有类的相关定义,但是我们可以使用结构体和方法的组合来实现类的相关特性。

1. 封装

我们可以通过结构体字段的首字母大小写控制访问权限,然后提供公共方法来操作私有字段。


在结构体中,大写开头的字段为公开字段,小写开头的字段为私有字段。


我们用下面的示例来展示一下用结构体和方法来实现封装功能。文件目录如下:


.├── main.go├── service│   └── person_operation.go
复制代码


其中 person_operation.go 的内容如下:


package service
type Person struct { Name string Age int gender string}
func (p *Person) SetGender(gender string) { p.gender = gender}
func (p *Person) GetGender() string { return p.gender}
复制代码


其中,Person 这个结构体的 Name 和 Age 字段首字母都为大写,为公共字段,而 gender 首字母为小写,在 main.go 里不能直接引用,所以下面定义了两个公有接口提供设置和访问。


以下是 main.go 里的内容:


package main
import ( "fmt" "go_proj/service")
func main() { person := service.Person{ Name: "张三", Age: 18, // gender: "男", // gender是私有属性,不能直接访问 } // fmt.Println(person.gender) // gender是私有属性,不能直接访问 fmt.Println(person.GetGender()) person.SetGender("男") fmt.Println(person.GetGender())}
复制代码


在这里,gender 字段在 Person 定义和访问的时候都不能直接操作,需要通过设置的方法来进行定义以及访问。

2. 继承

我们可以通过结构体的嵌套来实现继承,比如下面新建一个 Chinese 结构体:


type Chinese struct {    Person}
复制代码


然后我们定义的 Chinese 实例可以调用 Person 结构体的方法:


chinese := service.Chinese{    Person: service.Person{        Name: "张三",        Age:  18,    },}chinese.SetGender("男")fmt.Println(chinese.GetGender())
复制代码

3. 多态

多态则是同一方法名在不同的类型中有不同的实现,这个操作在前面介绍接口的就已经实现过了,这里不再做赘述。

发布于: 刚刚阅读数: 4
用户头像

Hunter熊

关注

公众号:Hunter后端 2018-09-17 加入

Python后端工程师,欢迎互相沟通交流

评论

发布
暂无评论
Golang基础笔记九之方法与接口_golang_Hunter熊_InfoQ写作社区