方法
方法是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={})
}
返回值不是一个地址
可寻址性
容器(数组、切片、映射)字面量和结构体字面量不可寻址,但是可以取地址
元素
- 切片元素都可寻址,不管切片是否可寻址,因为底层元素总是存在一块内存片段上(切片是生成数组后切分生成)
- 任何映射元素都不可寻址,映射内存可能会随着容量改变
- 数组元素是否可寻址取决于数组本身