28.方法
匿名字段
可以像字段成员那样访问匿名字段方法,编译器负责查找。
package main
import "fmt"
type User struct {
id int
name string
}
type Manager struct {
User //只定义类型
}
//用self来占位,也可以用其他表示,都会被编译器进行识别访问
func (self *User) ToString() string { // receiver = &(Manager.User)
return fmt.Sprintf("User: %p, %v", self, self)
}
func main() {
m := Manager{User{1, "Tom"}}
fmt.Printf("Manager: %p\n", &m)
fmt.Println(m.ToString())
}
Manager: 0xc000010030
User: 0xc000010030, &{1 Tom}
通过匿名字段,可获得和继承类似的复用能力。依据编译器查找次序,只需在外层定义同名方法,就可以实现 “override”。
package main
import "fmt"
type User struct {
id int
name string
}
type Manager struct {
User
title string
}
func (self *User) ToString() string {
return fmt.Sprintf("User: %p, %v", self, self)
}
func (self *Manager) ToString() string {
return fmt.Sprintf("Manager: %p, %v", self, self)
}
func main() {
m := Manager{User{1, "Tom"}, "Administrator"}
fmt.Println(m.ToString())
fmt.Println(m.User.ToString())
}
Manager: 0xc420074180, &{{1 Tom} Administrator}
User: 0xc420074180, &{1 Tom}
方法集
• 类型 T 方法集包含全部 receiver T 方法。
• 类型 *T 方法集包含全部 receiver T + *T 方法。
• 如类型 S 包含匿名字段 T,则 S 和 *S 方法集包含 T 方法。
• 如类型 S 包含匿名字段 *T,则 S 和 *S 方法集包含 T + *T 方法。
• 不管嵌入 T 或 *T,*S 方法集总是包含 T + *T 方法。
注:当类型调用自己声明的方法时,不需要考虑receiver是值语义还是指针语义,可以调用全部方法。因为在调用时,编译器自动做了转换。
// 使用值语义声明变量u
u := user{}
// notify方法的receiver为指针语义
u.notify() // 编译器自动转换为 (&u).notify()
// 使用指针语义声明变量u2
u2 := &user{}
// showUserInfo方法的receiver为值语义
u2.showUserInfo() // 编译器自动转换为 (*u2).showUserInfo()
// notifier 是一个定义了notify行为的接口
type notifier interface {
notify()
}
// user 使用指针语义实现了notifier接口
type user struct {
name string
email string
}
// receiver为指针语义
func (u *user) notify() {
fmt.Printf("Sending User Email To %s<%s>\n", u.name, u.email)
}
// receiver为值语义
func (u user) showUserInfo() {
fmt.Printf("Name:%s\n Email:<%s>\n", u.name, u.email)
}
func main() {
u := user{"Han", "han@email.com"}
// 发生编译错误!!!!!!!!!!!,此时T类型不包含*T的方法,所以报错
sendNotification(u)
//解决防范 sendNotification(&u)
}
// sendNotification函数接收一个实现notifier接口的变量
func sendNotification(n notifier) {
n.notify()
}
表达式
instance.method(args...) ---> <type>.func(instance, args...)
前者称为 method value,后者 method expression。
两者都可像普通函数那样赋值和传参,区别在于 method value 绑定实例,而 method expression 则须显式传参。
import "fmt"
type User struct {
id int
name string
}
func (self *User) Test() {
fmt.Printf("%p, %v\n", self, self)
}
func main() {
u := User{1, "Tom"}
u.Test()
mValue := u.Test
mValue() // 隐式传递 receiver,会自动去寻找对象,如匿名字段
mExpression := (*User).Test
mExpression(&u) // 显式传递 receiver
}
0xc42000a060, &{1 Tom}
0xc42000a060, &{1 Tom}
0xc42000a060, &{1 Tom}
method value会复制receiver
package main
import "fmt"
type User struct {
id int
name string
}
func (self User) Test() {
fmt.Println(self)
}
func main() {
u := User{1, "Tom"}
mValue := u.Test // 立即复制 receiver,因为不是指针类型,不受后续修改影响。
u.id, u.name = 2, "Jack"
u.Test() //{2 Jack}
mValue()//{1 Tom}
}
自定义error
package main
import (
"fmt"
"os"
"time"
)
type PathError struct {
path string
op string
createTime string
message string
}
func (p *PathError) Error() string {
return fmt.Sprintf("path=%s \nop=%s \ncreateTime=%s \nmessage=%s", p.path,
p.op, p.createTime, p.message)
}
func Open(filename string) error {
file, err := os.Open(filename)
if err != nil {
return &PathError{
path: filename,
op: "read",
message: err.Error(),
createTime: fmt.Sprintf("%v", time.Now()),
}
}
defer file.Close()
return nil
}
func main() {
err := Open("/Users/5lmh/Desktop/go/src/test.txt")
switch v := err.(type) {
case *PathError:
fmt.Println("get path error,", v)
default:
}
}