Golang_6_方法
目录:
六、方法
方法是与 对象实例绑定 的特殊函数,是一个面向对象的概念
方法的函数定义语法的区别:方法有前置实力接收参数
构造函数
建议返回结构体指针类型(值拷贝开销较大)
func newPerson(name, city string, age int8) *Person {
return &Person{
name: name,
city: city,
age: age,
}
}
调用构造函数
p := newPerson("Aris", "HB", 20)
fmt.Printf("%#v\n", p)
// &main.Person{name:"Aris", city:"HB", age:20}
方法 和 接受者
Go语言中的方法(Method)
是一种作用于特定类型变量的函数。这种特定类型变量叫做接收者(Receiver)
。
接收者的概念就类似于其他语言中的this
或者 self
。
func (接收者变量 接收者类型) 方法名(参数列表) (返回参数) {
函数体
}
- 接收者变量:接收者中的参数变量名在命名时,官方建议使用接收者类型名的第一个小写字母,而不是
self
、this
之类的命名。- 例如,
Person
类型的接收者变量应该命名为p
,Connector
类型的接收者变量应该命名为c
等。
- 例如,
- 接收者类型:接收者类型和参数类似,可以是指针类型和非指针类型。
- 方法名、参数列表、返回参数:具体格式与函数定义相同。
例子:
func newPerson(name string, age int8) *Person {
return &Person{
name: name,
age: age,
}
}
func (p Person) Dream() {
fmt.Printf("%s的梦想是学好Go!\n", p.name)
}
func main() {
p := newPerson("Aris", 20)
p.Dream()
// Aris的梦想是学好Go!
}
指针类型的接受者
用于修改实际的成员变量
func (p *Person) SetAge(newAge int8) {
p.age = newAge
}
调用:
func main() {
p := newPerson("Aris", 20)
fmt.Println(p.age) // 20
p.SetAge(30)
fmt.Println(p.age) // 30
}
值类型的接受者
当方法作用于值类型接收者时,Go语言会在代码运行时将接收者的值复制一份。
在值类型接收者的方法中可以获取接收者的成员值,但修改操作只是针对副本,无法修改接收者变量本身。
func (p Person) SetAge2(newAge int8) {
p.age = newAge
}
指针类型接受者使用场景
- 需要修改接受者的值
- 接受者是 拷贝代价比较大 的对象
- 一致性,如果有某个方法使用了指针接收者,那么其他的方法也应该使用指针接收者。
任意类型添加方法
不仅仅是结构体,任何类型都可以拥有方法
例子,int
类型添加方法
type MyInt int
func (m MyInt) SayHello() {
fmt.Println("i am a \"int\"")
}
func main() {
var m1 MyInt
m1 = 10
m1.SayHello() // i am a "int"
fmt.Printf("%#v %T\n", m1, m1)
//10 main.MyInt
}
注意事项: 非本地类型不能定义方法,也就是说我们不能给别的包的类型定义方法。
结构体匿名字段
定义结构体时,不写字段名只写类型:
type Person2 struct {
string
int
}
使用:
func main() {
p := Person2{
"Aris",
18,
}
fmt.Printf("%#v\n", p)
//main.Person2{string:"Aris", int:18}
fmt.Println(p.string, p.int)
// Aris 18
}
注意事项:
- 一个结构体中同种类型的匿名字段只能有一个,因为要保证字段名称的唯一性
嵌套结构体
type Address struct {
Province string
City string
}
type User struct {
Name string
Gender string
Address Address
}
func main() {
user := User{
Name: "Aris",
Gender: "男",
Address: Address{
Province: "HB",
City: "XG",
},
}
fmt.Printf("user=%#v\n", user)
//user=main.User{Name:"Aris", Gender:"男", Address:main.Address{Province:"湖北", City:"孝感"}}
// user=main.User{
// Name:"Aris",
// Gender:"男",
// Address: main.Address{
// Province:"HB",
// City:"XG"
// }
// }
}
“继承”
type Animal struct {
name string
}
func (a *Animal) move() {
fmt.Printf("%s会动!\n", a.name)
}
type Dog struct {
Feet int8
*Animal // 嵌套匿名结构体实现继承
}
func (d *Dog) bark() {
fmt.Printf("%s会汪汪!\n", d.name)
}
func main() {
d := &Dog{
Feet: 4,
Animal: &Animal{ // 嵌套的是结构体指针
name: "HuNiu",
},
}
d.move() // HuNiu会动!
d.bark() // HuNiu会汪汪!
}
结构体的字段可见性
结构体中:
- 字段大写开头表示可公开访问
- 小写表示私有(仅在定义当前结构体的包中可访问)。
结构体和JSON序列化(结构体和JSON互相转换)
package main
import (
"encoding/json"
"fmt"
)
//Student 学生
type Student struct {
ID int
Gender string
Name string
}
//Class 班级
type Class struct {
Title string
Students []*Student
}
func main() {
c := &Class{
Title: "101",
Students: make([]*Student, 0, 200),
//Students: []*Student{},
}
for i := 0; i < 10; i++ {
stu := &Student{
Name: fmt.Sprintf("stu%02d", i),
Gender: "男",
ID: i,
}
// c班级 中追加成员
c.Students = append(c.Students, stu)
}
// JSON序列化:结构体-->Json格式的字符串
data, err := json.Marshal(c)
if err != nil {
fmt.Println("JSON marshal failed")
return
}
fmt.Printf("JSON: %s\n", data)
// JSON反序列化:JSON格式的字符串-->结构体
str := `{"Title":"101","Students":[{"ID":0,"Gender":"男","Name":"stu00"},{"ID":1,"Gender":"男","Name":"stu01"},{"ID":2,"Gender":"男","Name":"stu02"},{"ID":3,"Gender":"男","Name":"stu03"},{"ID":4,"Gender":"男","Name":"stu04"},{"ID":5,"Gender":"男","Name":"stu05"},{"ID":6,"Gender":"男","Name":"stu06"},{"ID":7,"Gender":"男","Name":"stu07"},{"ID":8,"Gender":"男","Name":"stu08"},{"ID":9,"Gender":"男","Name":"stu09"}]}`
c1 := &Class{}
// 字符串str 反序列到结构体 c1
err = json.Unmarshal([]byte(str), c1)
if err != nil {
fmt.Println("JSON unmarshal failed!")
return
}
fmt.Printf("%#v\n", c1)
// &main.Class{Title:"101", Students:[]*main.Student{(*main.Student)(0xc42007c6f0), (*main.Student)(0xc42007c720), (*main.Student)(0xc42007c750), (*main.Student)(0xc42007c780), (*main.Student)(0xc42007c7e0), (*main.Student)(0xc42007c810), (*main.Student)(0xc42007c840), (*main.Student)(0xc42007c870), (*main.Student)(0xc42007c8a0), (*main.Student)(0xc42007c8d0)}}
结构体标签
自定义序列化时的key
-
Tag
是结构体的元信息,可以在运行的时候通过反射的机制读取出来。 -
Tag
在结构体字段的后方定义,由一对反引号包裹起来 -
结构体标签由一个或多个键值对组成
-
键与值使用冒号分隔,值用双引号括起来。
-
键值对之间使用一个空格分隔
-
需要严格按照格式来
结构体标签的解析代码的容错能力很差,一旦格式写错,编译和运行时都不会提示任何错误,通过反射也无法正确取值。
例如不要在key和value之间添加空格。
-
package main
import (
"encoding/json"
"fmt"
)
type Student struct {
// ID 通过指定 tag 实现 序列化该字段时使用的key
ID int `json:"id_NewName"`
Gender string // JSON序列化默认用字段名作Key
name string // 私有不能被JSON包访问
}
func main() {
s1 := Student{
ID: 1,
Gender: "男",
name: "Aris",
}
data, err := json.Marshal(s1)
if err != nil {
fmt.Println("json marshal failed!")
return
}
fmt.Printf("json str:%s\n", data) // json str:{"id":1,"Gender":"男"}
}
练习题
使用“面向对象”的思维方式编写一个学生信息管理系统。
- 学生有id、姓名、年龄、分数等信息
- 程序提供展示学生列表、添加学生、编辑学生信息、删除学生等功能
package main
import "fmt"
type Student struct {
Id int
Name string
Age int
Score int
}
type Class struct {
Title string
Student []*Student
}
// 展示学生列表
func (c *Class) ShowStudents() {
fmt.Println(c)
}
// 添加学生
func (c *Class) AddStudent(s *Student) {
c.Student = append(c.Student, s)
fmt.Printf("%d-%s 添加成功!\n", s.Id, s.Name)
}
// 编辑学生信息
func (s *Student) EditStudent(sTemp Student) {
}
func main() {
}