在编程语言中,方法与函数的概念来搞清楚。函数指的是一个封装的代码块,我们可以直接调用它,并且返回结果。而方法其实也是一种函数,只不过方法需要和某个对象绑定。Golang并没有类的概念,不过仍然有方法和接口这些概念。
方法
方法接收者
方法接收者是一个特殊参数,给函数指定了这个参数之后,函数就成为方法了。这个特性有点像Kotlin和C#中的扩展方法,定义了带有接收者的方法之后,接收者这个类型就好像定义了这个方法一样,我们可以直接在该类型上调用方法。这在功能上,和面向对象的概念是很类似的。
例如下面这样,定义了一个汽车结构,然后定义了一个接受者方法。然后就可以用面向对象的方式来调用这个方法了。
func Method() {
//方法接收者
car := Car{id: 1}
car.beep()
}
type Car struct {
id int
}
func (car Car) beep() {
fmt.Printf("Car %v beeps", car.id)
}
接收者方法也有一些限制,这也是它和扩展方法之间的区别。 接收者方法的接收者类型,必须和接收者方法定义在同一个包中。所以很多非自定义的类型,以及基本类型都不能当做接收者的类型。当然也可以投机取巧,在自己的包中重新为这些类型取个名字即可。
//把基本类型重新定义一下,就可以当做接收者类型了
type MyString string
func (str MyString) hello() {
fmt.Println("hello" + str)
}
指针接收者
接收者的类型可以是指针,如果希望在接收者方法中修改接收者的属性,就需要指针类型了。
package code
import "fmt"
type LinkedList struct {
size int
name string
}
func NewLinkedList()(*LinkedList) {
return &LinkedList{
size: 0,
name: "init",
}
}
func (c *LinkedList)Print() {
fmt.Println(c)
}
// 是指针,因此会修改指针的值
func (c *LinkedList)ChangeByP() {
c.size = 100;
c.name = "指针接收者"
}
// 不是在真正,不能修改指针的值
func (c LinkedList)ChangeNoP() {
c.size = -100;
c.name = "没有指针"
}
调用它
func main() {
var linklist *code.LinkedList
linklist = code.NewLinkedList()
fmt.Print("初始状态:\t")
linklist.Print()
fmt.Print("非指针修改(before):\t")
linklist.Print()
fmt.Print("非指针修改(after):\t")
linklist.ChangeNoP()
linklist.Print()
fmt.Print("通过指针修改(before):\t")
linklist.Print()
fmt.Print("通过指针修改(after):\t")
linklist.ChangeByP()
linklist.Print()
}
接口
听起来很奇怪,如果Golang没有类型,为什么会有接口的概念?让我们来看看Golang如何解决这些问题。
定义接口
在Golang中,接口就是一组方法签名的集合。下面就定义了一个接口
type List interface {
Get(index int) interface{};
Size() int
Append(value interface{})
}
实现接口
在golang中,其实并没有"实现接口"这一说法。在golang中接口是隐式实现的,也就是说我们不需要implements这些关键字。只要一个类型的接收者方法和接口的定义的方法(函数名、参数、返回值)一致,Golang就认为这个类型实现了该接口。
package code
import "errors"
type ArrayList struct {
size int // 数组的长度
elementData []interface{}
}
func (a *ArrayList) Get(index int) (interface{}, error) {
if index < 0 {
return nil, errors.New("不正确的参数")
}
if index >= a.size {
return nil, errors.New("超出索引范围")
}
return a.elementData[index], nil
}
func (a *ArrayList) Size() int {
return a.size
}
func (a *ArrayList) Append(value interface{}) {
a.elementData = append(a.elementData, value)
a.size++;
}
func NewArrayList() *ArrayList {
return &ArrayList{
size: 0,
elementData: nil,
}
}
调用:
func main() {
var list code.List
list = code.NewArrayList()
list.Append(1)
list.Append(2)
fmt.Println("显示:", list)
var arraylist code.ArrayList
arraylist.Append(3)
arraylist.Append(4)
arraylist.Append(3)
arraylist.Append(4)
fmt.Println("显示:", arraylist)
}
调用:
func main() {
var arraylist code.ArrayList
arraylist.Append(3)
arraylist.Append(4)
fmt.Println("显示:", arraylist)
var ilist code.List = &arraylist
fmt.Println("显示:", ilist)
}
空接口
- 在golang中,空接口
interface{}
是一个特殊的接口类型,因为它不包含任何方法签名 - 因此,所有的类型都实现了空接口。这对于需要存储任意类型的数据(比如map)非常有用
//空接口可以作为任何类型使用
type Everything interface {
}
var e Everything = "123"
fmt.Println(e)