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)類型的語法。

接口與類型的轉換還不知道

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