方法

方法是golang中的一个特性,方法可以看作是带有特殊接受者参数的函数,最常用的是为结构体定义方法,看起来就像面向对象里边的对象下的方法

package main


import (
"fmt"
)


type Book struct{}


func (b Book) SetPages() {
fmt.Println("SetPages")
}


func (b *Book) Pages() {
fmt.Println("Pages")
}

如示例,是为Book类型定义了SetPages方法、为*Book类型定义了Pages方法,既可以为指针定义方法,也可以为值类型定义方法,方法是带有特殊接收者参数的函数,所以上边定义与如下是相同的:

func  Book.SetPages(b Book) {
fmt.Println("SetPages")
}


func (*Book).Pages() {
fmt.Println("Pages")
}

但是在定义时是不合法的,没法这么定义,但是我们可以显式调用隐式定义

func (b Book) SetPages() {
fmt.Println("SetPages")
}


func (b *Book) Pages() {
fmt.Println("Pages")
}


func main() {
var b Book
Book.SetPages(b)  // 显式调用
(*Book).Pages(&b) // 显式调用
}

方法调用

调用方法时我们可以像面向对象调用对象下的方法一样,使用v.m的形式

type Book struct{}


func (b Book) SetPages() {
fmt.Println("SetPages")
}


func (b *Book) Pages() {
fmt.Println("Pages")
}


func main() {
b := Book{}
b1 := &b


b.SetPages()  // SetPages
b1.SetPages() // SetPages


b.Pages()  // Pages
b1.Pages() // Pages
}

为什么可以使用指针调用SetPages方法、值类型调用Pages方法呢?并没有为指针定义SetPages方法,也没有为值定义Pages方法啊。
原因在于golang为了方便,在设计的时候有考虑到,实际在使用指针调用SetPages方法时,golang会将其解释为*b1.SetPages()在使用值类型调用Pages方法时,golang会将其解释为&b.Pages()

如果我们这样写呢?

func (b Book) SetPages() {
fmt.Println("SetPages")
}


func (b *Book) Pages() {
fmt.Println("Pages")
}


func main() {


(Book{}).SetPages()  // SetPages
(&Book{}).SetPages() // SetPages


(&Book{}).Pages() // Pages
(Book{}).Pages()  // 报错
}

会发现调用(Book{}).Pages()会报错:

.\default.go:23:10: cannot call pointer method on Book literal

不能在字面量上调用指针方法,为什么不可以呢?和用变量调用的区别在哪呢?

原因

原因在于当接收者参数为指针类型时,接受者必须为可寻址的值类型,字面量结构体就是不可寻址的

func main() {


(Book{}).SetPages()  // SetPages
(&Book{}).SetPages() // SetPages


(&Book{}).Pages() // Pages
fmt.Printf("地址为:%p\n", Book{})// 地址为:%!p(main.Book={})
}

返回值不是一个地址

可寻址性

容器(数组、切片、映射)字面量和结构体字面量不可寻址,但是可以取地址

元素

  • 切片元素都可寻址,不管切片是否可寻址,因为底层元素总是存在一块内存片段上(切片是生成数组后切分生成)
  • 任何映射元素都不可寻址,映射内存可能会随着容量改变
  • 数组元素是否可寻址取决于数组本身