golang中的方法与接口

在编程语言中,方法与函数的概念来搞清楚。函数指的是一个封装的代码块,我们可以直接调用它,并且返回结果。而方法其实也是一种函数,只不过方法需要和某个对象绑定。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的概念,空接口被任何类型隐式实现,所以空接口可以容纳任何类型。

//空接口可以作为任何类型使用
type Everything interface {
}

var e Everything = "123"
fmt.Println(e)

类型细化[不理解]

定义和实现接口时一个类型泛化的过程,在这个过程中,我们抹消掉了类型特有的部分,让类型公有的部分能够统一利用。不过有时候需要反过来,将一个接口对象转换为原始的具体类,让我们能够获取更具体的行为。

现在来看看在Golang中,这件事情应该怎么做。再次使用上面定义的类型。可以看到和C系语言的括号强转方式不同,在Golang中是.(T)类型的语法。

接口与类型的转换还不知道

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章