视频来源:B站《golang入门到项目实战 [2022最新Go语言教程,没有废话,纯干货!]》
文章为自己整理的学习笔记,侵权即删,谢谢支持!
go语言没有面向对象的特性,也没有类对象的概念。但是,可以使用结构体来模拟这些特性,我们都知道面向对象里面有类方法等概念。我们也可以声明一些方法,属于某个结构体。
Go中的方法,是一种特殊的函数,定义于结构体struct之上(与struct关联、绑定),被称为struct的接受者(receiver)。通俗的讲,方法就是有接收者的函数。
type mytype struct{}
func (recv mytype) my_method(para) retrun_type {}
func (recv *mytype) my_method(para) return_type {}
mytype:定义的一个结构体
recv:接受该方法的结构体(receiver)
my_method:方法名称
para:参数列表
return_type:返回值类型
从语法格式可以看出,一个方法和一个函数非常相似,只不过多了一个接受类型。
package main
import "fmt"
type Person struct {
name string
}
func (per Person) eat() {
fmt.Println(per.name + " eating....")
}
func (per Person) sleep() {
fmt.Println(per.name + " sleep....")
}
func main() {
var per Person
per.name = "tom"
per.eat()
per.sleep()
}
运行结果:
tom eating....
tom sleep....
类型别名、slice、map、channel、func类型等都可以。(T Type)和(T *Type),它们之间有区别。结构体实例有值类型和指针类型,而方法的接收者是结构体,那么也有值类型和指针类型。区别就是接收者是否复制结构体副本。
值类型复制,指针类型不复制。
先看一段代码:
package main
import "fmt"
type Person struct {
name string
}
func main() {
p1 := Person{name: "tom"}
fmt.Printf("p1: %T\n", p1)
p2 := &Person{name: "tom"}
fmt.Printf("p2: %T\n", p2)
}
运行结果:
p1: main.Person
p2: *main.Person
从运行结果,我们可以看出p1是值类型,p2是指针类型。
下面看一个传参结构体的例子:
package main
import "fmt"
type Person struct {
name string
}
func showPerson(per Person) {
fmt.Printf("per: %p\n", &per)
per.name = "kite"
fmt.Printf("per: %v\n", per)
}
func showPerson2(per *Person) {
fmt.Printf("per: %p\n", per)
// (*per).name
per.name = "kite"
fmt.Printf("per: %v\n", per)
}
func main() {
p1 := Person{name: "tom"}
fmt.Printf("p1: %p\n", &p1)
showPerson(p1)
fmt.Printf("p1: %v\n", p1)
fmt.Println("---------------------")
p2 := &Person{name: "tom"}
fmt.Printf("p2: %p\n", p2)
showPerson2(p2)
fmt.Printf("p2: %v\n", p2)
}
运行结果:
p1: 0xc000050230
per: 0xc000050240
per: {kite}
p1: {tom}
---------------------
p2: 0xc000050270
per: 0xc000050270
per: &{kite}
p2: &{kite}
从运行结果,我们看到p1是值传递,拷贝了副本,地址发生了改变;而p2是指针类型,地址没有改变。
值类型和指针类型接收者本质上和函数传参道理相同。
package main
import "fmt"
type Person struct {
name string
}
func (per Person) showPerson() {
fmt.Printf("per: %p\n", &per)
per.name = "kite"
fmt.Printf("per: %v\n", per)
}
func (per *Person) showPerson2() {
fmt.Printf("per: %p\n", per)
per.name = "kite"
fmt.Printf("per: %v\n", per)
}
func main() {
p1 := Person{name: "tom"}
fmt.Printf("p1: %p\n", &p1)
p1.showPerson()
fmt.Printf("p1: %v\n", p1)
fmt.Println("---------------------")
p2 := &Person{name: "tom"}
fmt.Printf("p2: %p\n", p2)
p2.showPerson2()
fmt.Printf("p2: %v\n", p2)
}
运行结果:
p1: 0xc000050230
per: 0xc000050240
per: {kite}
p1: {tom}
---------------------
p2: 0xc000050270
per: 0xc000050270
per: &{kite}
p2: &{kite}
调用方式不一样
函数的调用方式:
函数名(实参列表)
方法的调用方式:变量.方法名(实参列表)
对于普通函数,接收者为值类型时,不能将指针类型的数据直接传递,反之亦然
对于方法(如 struct 的方法),接收者为值类型时,可以直接用指针类型的变量调用方法,反过来同样也可以
① 编写结构体(MethodUtils),编程一个方法,方法不需要参数,在方法中打印一个 10*8 的矩形,在 main 方法中调用该方法。
package main
import (
"fmt"
)
type Methodutiles struct {
}
func (mu Methodutiles) Print() {
for i := 1; i <= 10; i++ {
for j := 1; j <= 8; j++ {
fmt.Print("*")
}
fmt.Println()
}
}
func main() {
var mu Methodutiles
mu.Print()
}
运行结果:
********
********
********
********
********
********
********
********
********
********
② 定义小小计算器结构体(Calcuator),实现加减乘除四个功能
实现形式 1:分四个方法完成;
实现形式 2:用一个方法搞定。
实现形式1:
type Calcuator struct {
Num1 float64
Num2 float64
}
// 实现形式1:
func (cal *Calcuator) getSum() float64 {
return cal.Num1 + cal.Num2
}
func (cal *Calcuator) getSub() float64 {
return cal.Num1 - cal.Num2
}
func (cal *Calcuator) getPro() float64 {
return cal.Num1 * cal.Num2
}
func (cal *Calcuator) getQuo() float64 {
return cal.Num1 / cal.Num2
}
实现形式2:
type Calcuator struct {
Num1 float64
Num2 float64
}
func (cal *Calcuator) getRes(operator byte) float64 {
res := 0.0
switch operator {
case '+':
res = cal.Num1 + cal.Num2
case '-':
res = cal.Num1 - cal.Num2
case '*':
res = cal.Num1 * cal.Num2
case '/':
res = cal.Num1 / cal.Num2
default:
fmt.Println("运算符输入错误")
}
return res
}