Skip to content

1. golang方法

go语言没有面向对象的特征,也没有类对象的概念。但是,可以使用结构体来模拟这些特征,我们都知道面向对象里面有类方法等概念。我们可以声明一些方法,属于某个结构体。

在面向对象编程中,一个对象其实也就是一个简单的值或者一个变量,在这个对象中会包含一些函数,这种带有接收者的函数,我们称之为方法(method)。本质上,一个方法则是一个和特殊类型关联的函数。

一个面向对象的程序会用方法来表达其属性和对应的操作,这样使用这个对象的用户就不需要直接去操作对象,而是借助方法来做这些事情。

在 Go 语言中,可以给任意自定义类型(包括内置类型,但不包括指针类型)添加相应的方法。

方法总是绑定对象实例,并隐式将实例作为第一实参(receiver)

1.1 go语言放的语法

Go中的方法,是一种特殊的函数,定义于struct之上(与struct关联、绑定),被称为struct的接受者(receiver)。

通俗的讲,方法就是有接受者的函数。

  • 语法格式:
go
type mytype struct{}

func (recv mytype)my_method(para) return_type{}

func (recv *mytype)my_method(para) return_type{}
type mytype struct{}

func (recv mytype)my_method(para) return_type{}

func (recv *mytype)my_method(para) return_type{}

mytype:定义一个结构体

recv:接受该方法的结构体(receiver)

my_method:方法名称

para:参数列表

return_type:返回值类型

从语法格式可以看出,一个方法和一个函数非常相似,多了一个接受类型

go
package main

import "fmt"

// 属性
type Person struct {
	name string
}

// 方法
// (per Person)接收者 receiver
func (per Person) eat() {
	fmt.Printf("eat...: %v\n", per.name)
}

func (per Person) sleep() {
	fmt.Printf("sleep...: %v\n", per.name)
}

// other
type Customer struct {
	name string
}

func (customer Customer) login(name string, pwd string) bool {
	if name == "tom" && pwd == "123" {
		return true
	} else {
		return false
	}
}

func main() {
	cus := Customer{
		"tom",
	}
	logins := cus.login("tom", "123")
	fmt.Printf("logins: %v\n", logins)
	/*per := Person{
		"tom",
	}
	per.eat()
	per.sleep()*/
}
package main

import "fmt"

// 属性
type Person struct {
	name string
}

// 方法
// (per Person)接收者 receiver
func (per Person) eat() {
	fmt.Printf("eat...: %v\n", per.name)
}

func (per Person) sleep() {
	fmt.Printf("sleep...: %v\n", per.name)
}

// other
type Customer struct {
	name string
}

func (customer Customer) login(name string, pwd string) bool {
	if name == "tom" && pwd == "123" {
		return true
	} else {
		return false
	}
}

func main() {
	cus := Customer{
		"tom",
	}
	logins := cus.login("tom", "123")
	fmt.Printf("logins: %v\n", logins)
	/*per := Person{
		"tom",
	}
	per.eat()
	per.sleep()*/
}
  • go语言方法的注意事项

1.方法的receiver type并非一定是struct类型,type定义的类型别名、slice、map、channel、func类型等都可以

2.struct结合它的方法就等价于面向对象中的类。只不过struct可以和它的方法拆开,并非一定要属于同一个文件,但必须属于同一个包。

3.方法有两种接受类型:(T Type)和(T *Type),它们之间有区别。

4.方法就是函数,所以Go中中没有方法重载(overload)的说法,也就是说同一个类型中的所有方法名必须都唯一。

5.如果receiver是一个指针类型,则会自动解除引用

6.方法和type是分开的,意味着实例的行为(behavior)和数据存储(field)是分开的,但是它们通过receiver建立起关联关系。

1.2 go方法接收者类型

结构体实例,有值类型和指针类型,那么方法的接受者是结构体,那么有值类型和指针类型。区别就是接收者是否复制结构体副本。值类型复制,指针类型不复制。

go
package main

import "fmt"

func main() {
	type Person struct {
		name string
	}
	p1 := Person{
		"tom",
	}
	p2 := &Person{
		"tom",
	}
	fmt.Printf("p1: %T\n", p1)
	fmt.Printf("p2: %T\n", p2)
}

#输出结果
p1: main.Person
p2: *main.Person
package main

import "fmt"

func main() {
	type Person struct {
		name string
	}
	p1 := Person{
		"tom",
	}
	p2 := &Person{
		"tom",
	}
	fmt.Printf("p1: %T\n", p1)
	fmt.Printf("p2: %T\n", p2)
}

#输出结果
p1: main.Person
p2: *main.Person
  • 传参结构体例子
go
package main

import "fmt"

func test1() {
	type Person struct {
		name string
	}
	p1 := Person{
		"tom",
	}
	p2 := &Person{
		"tom",
	}
	fmt.Printf("p1: %T\n", p1)
	fmt.Printf("p2: %T\n", p2)
}

type Person1 struct {
	name string
}

func showPerson(per Person1) {
	per.name = "tom"
}
func showPerson2(per *Person1) {
	//per.name自动解引用
	per.name = "tom"
}
func main() {
	p1 := Person1{
		"tom...",
	}
	p2 := &Person1{
		"tom...",
	}
	showPerson(p1)
	fmt.Printf("p1: %v\n", p1)
	fmt.Println("----------------------")
	showPerson2(p2)
	fmt.Printf("p2: %v\n", *p2)
	//从结果看,p1是值传递,拷贝了副本,本地发生了改变,而p2是指针类型,地址没有变化
}

#结果
p1: {tom...}
----------------------
p2: {tom}
package main

import "fmt"

func test1() {
	type Person struct {
		name string
	}
	p1 := Person{
		"tom",
	}
	p2 := &Person{
		"tom",
	}
	fmt.Printf("p1: %T\n", p1)
	fmt.Printf("p2: %T\n", p2)
}

type Person1 struct {
	name string
}

func showPerson(per Person1) {
	per.name = "tom"
}
func showPerson2(per *Person1) {
	//per.name自动解引用
	per.name = "tom"
}
func main() {
	p1 := Person1{
		"tom...",
	}
	p2 := &Person1{
		"tom...",
	}
	showPerson(p1)
	fmt.Printf("p1: %v\n", p1)
	fmt.Println("----------------------")
	showPerson2(p2)
	fmt.Printf("p2: %v\n", *p2)
	//从结果看,p1是值传递,拷贝了副本,本地发生了改变,而p2是指针类型,地址没有变化
}

#结果
p1: {tom...}
----------------------
p2: {tom}

1.3 方法的值类型和指针类型接受者

值类型和指针类型接受者,本质上和函数传参道理相同

go
package main

import "fmt"

type Person1 struct {
	name string
}



func (per Person1) showPerson3() {
	per.name = "tom"
}
func (per *Person1) showPerson4() {
	//per.name自动解引用
	per.name = "tom"
}

func main() {
	p1 := Person1{
		"tom...",
	}
	p2 := &Person1{
		"tom...",
	}
	
	//从结果看,p1是值传递,拷贝了副本,本地发生了改变,而p2是指针类型,地址没有变化
	p1.showPerson3()
	fmt.Printf("p1: %v\n", p1)
	fmt.Println("----------------------")
	p2.showPerson4()
	fmt.Printf("p2: %v\n", *p2)
}
package main

import "fmt"

type Person1 struct {
	name string
}



func (per Person1) showPerson3() {
	per.name = "tom"
}
func (per *Person1) showPerson4() {
	//per.name自动解引用
	per.name = "tom"
}

func main() {
	p1 := Person1{
		"tom...",
	}
	p2 := &Person1{
		"tom...",
	}
	
	//从结果看,p1是值传递,拷贝了副本,本地发生了改变,而p2是指针类型,地址没有变化
	p1.showPerson3()
	fmt.Printf("p1: %v\n", p1)
	fmt.Println("----------------------")
	p2.showPerson4()
	fmt.Printf("p2: %v\n", *p2)
}

2. 面向对象和面向过程

go
package main

import "fmt"

//实现2数相加
//面向过程
func Add01(a, b int) int {
	return a + b

}

//面向对象,通过方法,就是给某个类型绑定一个函数
type long int

//a 叫接收者,接收者就是传递一个参数
func (a long) Add02(b long) long {
	return a + b
}

func main() {
	var s1 int
	s1 = Add01(2, 3)
	fmt.Println("s1 = ", s1)

	//定义一个变量
	var s2 long = 2
    
	//调用方法格式: 变量名.函数(所需参数)
	r := s2.Add02(3)
	fmt.Println("r = ", r)
	//面向对象只是换了一种表现形式
}
package main

import "fmt"

//实现2数相加
//面向过程
func Add01(a, b int) int {
	return a + b

}

//面向对象,通过方法,就是给某个类型绑定一个函数
type long int

//a 叫接收者,接收者就是传递一个参数
func (a long) Add02(b long) long {
	return a + b
}

func main() {
	var s1 int
	s1 = Add01(2, 3)
	fmt.Println("s1 = ", s1)

	//定义一个变量
	var s2 long = 2
    
	//调用方法格式: 变量名.函数(所需参数)
	r := s2.Add02(3)
	fmt.Println("r = ", r)
	//面向对象只是换了一种表现形式
}