Go笔记:方法

Posted 无虑的小猪

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Go笔记:方法相关的知识,希望对你有一定的参考价值。

  Go语言没有Java语言面向对象的特性,也无类对象的概念。但可以使用结构体实现这些特性。

1、方法的声明

  Go中的方法是一种特殊的函数,与struct相关联,被称为struct的接收者。可以理解为方法就是有接收者的函数。语法格式如下:

type mystruct struct
func (recv mystruct) my_method(para) return_type
func (recv *mystruct) my_method(para) return_type

  mystruct:定义的结构体

  recv :接受该方法的结构体

  my_method:方法名称

  para:方法参数列表

  return_type:方法返回值类型

示例代码如下:

 package main
 
 import "fmt"
 
 // 结构体
 type Book struct 
    bookName string
 
 
 // 方法
 // 比函数多了一个接受者,接受者为结构体
 func (bk Book) info(name string) bool 
    fmt.Printf("book.bookName:%v is very good\\n", bk.bookName)
    return true;
 
 
 func main() 
    book := Book"平凡的世界"
    book.info(book.bookName)
 

2、接收者类型

  接收者类型有值类型和指针类型。区别在于接收者是否需要复制结构体副本。值类型复制副本,指针类型不复制副本。

2.1、值类型

  以接收者是结构体为例,值类型示例代码如下:

 package main
 
 import "fmt"
 
 // 结构体
 type Book struct 
    bookName string
 
 
 // 比函数多了一个接受者,接受者为结构体,值类型接受者
 func (bk Book) valueType() 
    bk.bookName = bk.bookName + " valueType"
 
 
 func main() 
   
    bk := Book"平凡的世界"
    // 值作为接受者
    bk.valueType()
    fmt.Printf("valueType: %v\\n", bk)
 

2.2、指针类型

  以接收者是结构体为例,指针类型示例代码如下:

 package main
 
 import "fmt"
 
 // 结构体
 type Book struct 
    bookName string
 
 
 // 比函数多了一个接受者,接受者为结构体,指针类型接受者
 func (bk *Book) pointerType() 
    bk.bookName = bk.bookName + " pointerType"
 
 
 func main() 
 
    bk := Book"平凡的世界"
    // 指针作为接受者
    bk.pointerType()
    fmt.Printf("pointerType: %v\\n", bk)
 

3、实现OOP的属性和方法

  Go语言无面向对象的概念,也无封装的概念,但可通过结构体struct和方法来实现OOP的属性和方法等特性

 package main
 
 import "fmt"
 // 结构体 等价于 对象 ; name  等价于  属性
 type Person struct 
    name string
 
 // receiver 接收者方法  等价于  方法
 func (per Person) eat() 
    fmt.Printf("run method: %v eat \\n", per.name)
 
 func main() 
    // 等价于Java中的 new 对象
    per := Person"zs"
    // 输出属性
    fmt.Printf("per.name: %v\\n", per.name)
    // 执行方法
    per.eat()
 

4、方法的注意事项

  1、struct结合其方法等价于面向对象的方法。struct可以与其方法分开,并非一定同属一个文件,但必须同属一个包;

  2、方法两种接收类型:(T type)和(T *type);

  3、方法就是函数,Go中无方法重载的概念,所以在Go中所用方法名必须唯一;

  4、若receiver是一个指针类型,则会自动解除引用;

  5、方法和type分开,表示实例的行为与数据存储是分开的,是通过receiver建立的关联关系。

 

go语言学习笔记 — 基础 — 复合数据类型 — 结构体:结构体方法(行为)

go中的方法是一种作用于特定类型变量(也即接收器receiver)的函数。如果把特定类型理解为结构体或类时,接收器receiver类似于python中的self。

在go中,接收器receiver可以是任何类型和结构体;任何类型都可以拥有方法。

在面向对象编程范式中,类拥有的方法一般理解为类可以做的事情。go中的方法也是这个意思。在go中,方法的作用对象是接收器(也即类型实例),而函数没有作用对象。


1. 接收器 —— 方法的作用对象

  • 接收器格式
func (接收器变量 接收器类型) 方法名(参数列表) (返回参数) {
    函数体
}

接收器变量:建议使用接收器类型名的第一个小写字母,而不是self、this。例如Socket类型接收器变量应该命令为s,Connector类型接收器变量命名为c。

接收器类型:与参数类似,可以是指针类型和非指针类型。

方法名、参数列表、返回参数:格式与函数定义一致。

根据类型,接收器可分为指针接收器、非指针接收器。两种接收器会有不同的使用效果。

  • 指针类型接收器(常用)

指针类型的接收器由一个结构体指针组成,更接近面向对象中的this或self。

由于指针特性,调用方法时,修改接收器指针任意成员变量,方法结束后,修改都是有效记忆的。

package main

import "fmt"

// 定义结构体Property
type Property struct {
    value int               // 拥有一个整型的成员变量
}

// 定义修改字段值的方法
func (p *Property) SetValue(v int) {
    p.value = v             // 没有返回值,就没有返回类型
}

// 定义获取字段值的方法
func (p *Property) GetValue() int {
    return p.value
}

func main() {
    p := new(Property)   //实例化Property结构体,得到结构体指针类型*T
    p.SetValue(100)           // 调用Setvalue方法,修改属性值
    fmt.Println(p.GetValue()) // 调用Getvalue方法,获取属性值
}
  • 非指针类型接收器

当方法作用于非指针类型接收器时,go会在代码运行时把接收器的值复制一份。在非指针类型接收器的方法中,我们可以获取接收器的成员值,但修改后无效。

Point为小内存对象,在函数返回值复制时,可以极大提高代码运行效率。

package main

import (
    "fmt"
)

// 定义点结构体,包含x,y两个分量
type Point struct {
    X int
    Y int
}

// 为结构体Point定义一个Add方法,传入和传出都是Point结构体,实现多点连续相加,如p1.Add(p2).Add(p3)
func (p Point) Add(other Point) Point {
    return Point{p.X + other.X, p.Y + other.Y}
}

func main() {
    p1 := Point{1, 1}    // 初始化两个点p1,p2
    p2 := Point{2, 2}
    
    result := p1.Add(p2) // 两点相加
    
    fmt.Println(result)  // 输出结果
}

在计算机中,小内存对象由于值复制时的速度较快,所以适合使用非指针类型接收器;大内存对象因为值复制性能较差,适合使用指针类型接收器,在接收器和参数间传递时,不进行值复制,而只是传递指针。


2. 为结构体类型添加(绑定)方法

把背包作为“对象”,把物品放入背包的过程作为“方法”。

  • 面向过程编程实现

面向过程没有“方法”,只能通过结构体和函数,使用函数入参和函数调用,模拟“方法”。

type Bag struct {
    items []int
}

// 定义函数Insert,把一个物品放入背包
func Insert(b *Bag, itemid int) {//背包指针*Bag,物品ID(itemid)
    b.items = append(b.items, itemid)// 使用append把物品ID加到Bag的items成员变量,模拟向背包加东西
}

func main() {
    bag := new(Bag)  // 创建Bag实例bag,得到结构体指针类型*Bag
    Insert(bag, 1001) // 调用Insert函数,入参bag,1001
}

Insert()函数把*Bag类型的参数放在首位,*Bag结构体为操作对象。

  • 面向对象编程实现

把背包和放入物品到背包中的过程,使用结构体和方法实现。

type Bag struct {
    items []int
}

// 为Bag类型添加Insert方法
func (b *Bag) Insert(itemid int) {
    b.items = append(b.items, itemid)
}

func main() {
    b := new(Bag)   // 实例化结构体Bag,得到*Bag类型的b
    b.Insert(1001)  // 对操作对象b,调用方法Insert
}

定义方法Insert,作用对象是接收器(b *Bag)。每个方法只能有一个接收器,func (b *Bag) Insert(itemid int)中,(b *Bag)是方法Insert的接收器,为结构体添加方法。


3. 为任何类型添加方法

go可以为任何类型添加方法。给类型添加方法,就像给结构体添加方法一样,因为结构体也是一种类型。

  • 为基本类型添加方法

go使用type关键字定义新的自定义类型,之后就可以给自定义类型添加各种方法。

判断一个值是否为0,下面给出面向过程和面向对象两种方式:

if v == 0 {
    express // v等于0  
}

if v.IsZero() {   // 把v作为整型对象,可以增加一个IsZero方法
    express // v等于0  
}
package main

import "fmt"

// 自定义类型MyInt
type MyInt int

// 为Myint类型添加IsZero方法,(m MyInt)为非指针接收器,数值类型没有必要使用指针接收器
func (m MyInt) IsZero() bool {
	return m == 0
}

// 为MyInt类型添加Add()方法
func (m MyInt) Add(other int) int {
	return other + int(m)    // 把m从MyInt类型转换成int类型
}

func main() {
	var b MyInt
	fmt.Println(b.IsZero()) // 调用b的IsZero方法,由于使用非指针接收器,b的值会被复制到IsZero方法进行判断

	b = 1
	fmt.Println(b.Add(2))   // 调用b的Add方法
}

// true
// 3

以上是关于Go笔记:方法的主要内容,如果未能解决你的问题,请参考以下文章

go语言结构体及方法的一些细节笔记

《Go学习笔记 . 雨痕》方法

go语言学习笔记 — 基础 — 复合数据类型 — 结构体:结构体方法(行为)

《Go语言精进之路,从新手到高手的编程思想方法和技巧1》读书笔记和分享

《Go语言精进之路,从新手到高手的编程思想方法和技巧1》读书笔记和分享

Go语言核心36讲(Go语言进阶技术七)--学习笔记