Go 方法

什么是方法:

方法其实就是一个函数,在func这个关键字和方法名中间加入了一个特殊的接收器类型,接收器可以是结构体类型或者是非结构体类型,接收器是可以在方法的内部访问的。

下面就是创建一个方法的语法:

Go 复制代码
func (t Tpye) methodName(parameter list) { }

上面的代码片段创建了一个接收器类型为Type的方法 methodName

示例:

在Employee结构体类型上创建一个displaySalary方法,displaySalart()方法在方法的内部访问了接收器e Employee

Go 复制代码
package main

import (
    "fmt"
)

type Employee struct {
    name     string
    salary   int
    currency string
}

func (e Employee) displaySalary() {
    fmt.Printf("Salary of %s is %s%d", e.name, e.currency, e.salary)
}

func main() {
    employ1 := Employee{
        name:     "wang",
        salary:   5000,
        currency: "$",
    }
    employ1.displaySalary()
}

输出:

Go 复制代码
Salary of wang is $5000

为什么已经有函数了还需要方法:

将上面的例子改写成函数:

Go 复制代码
package main

import (
    "fmt"
)

type Employee struct {
    name     string
    salary   int
    currency string
}

func displaySalary(e Employee) {
    fmt.Printf("Salary of %s is %s%d", e.name, e.currency, e.salary)
}

func main() {
    employ1 := Employee{
        name:     "wang",
        salary:   5000,
        currency: "$",
    }
    displaySalary(employ1)
}

输出:

Go 复制代码
Salary of wang is $5000

在上面的程序中,displaySalart 方法被转化为一个函数,Employee 结构体被当做参数传递给它,这个程序也产生完全相同的输出

为什么需要方法,有几个原因:

1、Go 不是纯粹的面向对象的编程语言,而且Go不支持类,因此,基于类型的方法是一种实现和类相似行为的途径。

2、相同的名字的方法可以定义在不同类型上,而相同名字的函数是不允许的。

指针接收器和值接收器

GO 方法里面还可以创建指针接收器的方法,值接收器和指针接收器的区别在于,在指针接收器的方法内部的改变对于调用者是可见的,然而值接收器的情况不是这样的

理解:值接收器传递的只是一个值,而指针接收器传递的是一个地址,指向该值的地址

如何选择指针接收器还是值接收器:

1、修改接收器状态:如果方法需要修改接收器的状态,Name通常应该使用指针接收器,这是因为指针接收器可以直接修改接收器指向的对象,而值接收器只是接收对象的副本,对其修改不会影响原始对象。

2、避免值拷贝:如果接收器的类型是较大的结构体或包含了大量数据的类型,而方法不需要修改接收器状态,但需要读取接收器的属性,那么应该考虑使用指针接收器,这可以避免在方法调用时发生不必要的值拷贝,提高程序的性能

3、一致性:如果在类型的方法中有一些方法需要修改接收器状态,而其他方法只是需要读取接收器的属性,那么应该保持一致性,统一使用指针接收器。

4、可空类型:如果类型的零值是合法的,并且在类型的实例在某些情况下可能为nil,那么应该使用指针接收器,这样可以更好处理nil接收器的情况,避免因为nil接收器调用方法而引发panic

示例:

Go 复制代码
package main

import (
    "fmt"
)

type Employee struct {
    name string
    age  int
}

// 使用值接收器的方法
func (e Employee) changeName(newName string) {
    e.name = newName
}

// 使用指针接收器的方法
func (e *Employee) changeAge(newAge int) {
    e.age = newAge
}

func main() {
    e := Employee{
        name: "wang",
        age:  20,
    }
    fmt.Printf("Employee name before change: %s", e.name)
    e.changeName("zhang")
    fmt.Printf("\nEmployee name after change: %s", e.name)
    fmt.Printf("\n\nEmployee age before change: %d", e.age)
    (&e).changeAge(21)
    fmt.Printf("\nEmployee age after change: %d", e.age)
}

输出:

Go 复制代码
Employee name before change: wang 
Employee name after change: wang 
Employee age before change: 20 
Employee age after change: 21

在上面的程序中,changeName方法有一个值接收器(e Employee),而changeAge 方法有一个指针接收器(e *Employee). 在changeName 方法中对Employee结构体的字段name所做的改变对调用者是不可见的,因此在程序调用e.chanName("zhang")这个方法前后打印出相同的名字,由于changeAge方法值使用指针(e *Employee)接收器的,所以在调用(&e).changeAge方法对age字段做出的改变对调用者将是可见的。

在上面的程序里使用了 (&e).changeAge(21)来调用changeAge方法,由于changeAge方法有一个指针接收器,所以使用(&e)来调用这个方法,其实没有这个必要,Go语言让我们可以直接使用e.changeAge(21),会被自动解释为 (&e).changeAge(21)

在方法中使用指针接收器和在函数中使用指针参数

和值参数类似,函数使用指针参数只接受指针,而使用指针接收器的方法可以使用值接收器和指针接收器

示例:

Go 复制代码
package main

import (
    "fmt"
)

type rectangle struct {
    length int
    width  int
}

func perimeter(r *rectangle) {
    fmt.Println("perimeter function output:", 2*(r.length+r.width))
}

func (r *rectangle) perimeter() {
    fmt.Println("perimeter method output:", 2*(r.length+r.width))
}

func main() {
    r := rectangle{
        length: 10,
        width:  5,
    }
    p := &r
    perimeter(p)
    p.perimeter()
    r.perimeter()
}

输出:

Go 复制代码
perimeter function output: 30 
perimeter method output: 30 
perimeter method output: 30

我们通过值接收器 r 来调用有指针接收器的方法 perimeter。这是被允许的,为了方便Go语言把代码 r.perimeter() 解释为 (&r).perimeter()。

在非结构体上的方法

Go 复制代码
package main

import (
    "fmt"
)

type myInt int

func (a myInt) add(b myInt) myInt {
    return a + b
}

func main() {
    num1 := myInt(5)
    num2 := myInt(10)
    sum := num1.add(num2)
    fmt.Println("Sum is", sum)
}

上面的程序中,为int类型创建了一个类型别名myInt,定义了一个以myInt为接收器的方法add

输出:

Go 复制代码
Sum is 15

参考:

Go 系列教程 ------ 17. 方法 - Go语言中文网 - Golang中文社区

相关推荐
Dontla26 分钟前
Rust泛型系统类型推导原理(Rust类型推导、泛型类型推导、泛型推导)为什么在某些情况必须手动添加泛型特征约束?(泛型trait约束)
开发语言·算法·rust
java小吕布1 小时前
Java中的排序算法:探索与比较
java·后端·算法·排序算法
Neophyte06081 小时前
C++算法练习-day40——617.合并二叉树
开发语言·c++·算法
Goboy1 小时前
工欲善其事,必先利其器;小白入门Hadoop必备过程
后端·程序员
慕容复之巅1 小时前
基于MATLAB的条形码的识别图像处理报告
开发语言·图像处理·matlab
zqzgng1 小时前
Python 数据可视化pilot
开发语言·python·信息可视化
写bug的小屁孩1 小时前
websocket初始化
服务器·开发语言·网络·c++·websocket·网络协议·qt creator
Dr_eamboat1 小时前
【Java】枚举类映射
java·开发语言·python
李少兄2 小时前
解决 Spring Boot 中 `Ambiguous mapping. Cannot map ‘xxxController‘ method` 错误
java·spring boot·后端
代码小鑫2 小时前
A031-基于SpringBoot的健身房管理系统设计与实现
java·开发语言·数据库·spring boot·后端