該文內容來看讀《Go併發編程實戰》有感,僅供娛樂分享 :)編程
在%GOROOT%\src\sort包下有一個sort.go文件,裏面第12行有這麼一個接口定義:併發
type Interface interface {ide
// Len is the number of elements in the collection.函數
Len() int測試
// Less reports whether the element withspa
// index i should sort before the element with index j.指針
Less(i, j int) boolorm
// Swap swaps the elements with indexes i and j.排序
Swap(i, j int)接口
}
該接口定義了3個方法:Len()、Less()、Swap(),分別用以求長度、比大小和交換元素使用。
下面自定義一個數據類型,來實現這個接口:
import (
"fmt"
"sort"
)
type SortableStrings [3]string // 自定義一個數據類型SortableString
func (s SortableStrings) Len() int { // 非***式地實現三個接口的Len()方法
return len(s)
}
func (s SortableStrings) Less(i, j int) bool { // 非***式地實現三個接口的Less()方法
return s[i] < s[j]
}
func (s SortableStrings) Swap(i, j int) { // 非***式地實現三個接口的Swap()方法
s[i], s[j] = s[j], s[i]
}
func main() {
// 斷言SortableStrings類型是否已是sort.Interface接口的一個實現?
_, ok := interface{}(SortableStrings{}).(sort.Interface)
fmt.Println(ok) // 這裏打印是的,由於的確非***式地實現了sort.Interface接口中的全部方法
}
再定義一個接口唄,反正也不花錢
type Sortable interface {
sort.Interface
Sort()
}
書中舉這個例子,就是想說明能夠把一個接口類型(sort.Interface)嵌入到另外一個接口類型(Sortable)中。
一個自定義數據類型,是否能夠實現多個接口?
是的,能夠,因此書中的SortableStrings又實現了Sort()方法:
func (s SortableStrings) Sort() {
sort.Sort(s) // 使用go標準庫中的Sort()方法進行排序
}
func main() {
// 斷言SortableStrings類型是否已是Sortable接口的一個實現?
_, ok := interface{}(SortableStrings{}).(sort.Interface)
fmt.Println(ok)
_, ok2 := interface{}(SortableStrings{}).(Sortable)
fmt.Println(ok2) // 這裏打印是的true
}
這個示例就是想進一步說明一個自定義類型能夠同時實現多個接口。
書中又說,既然你都實現了,那麼運行一下唄,反正你也不是真懂 :)
func main() {
_, ok := interface{}(SortableStrings{}).(sort.Interface)
fmt.Println(ok)
_, ok2 := interface{}(SortableStrings{}).(Sortable)
fmt.Println(ok2)
ss := SortableStrings{"2", "3", "1"}
ss.Sort()
fmt.Printf("Sortable strings: %v\n", ss)
}
執行一打印,發現並無排序成功:
Sortable strings: [2 3 1]
這是怎麼回事呢?
書中原話,現摘錄在下面:
咱們在上一小節說過,在值方法中,對接收者的值的改變在該方法以外是不可見的。在上面的示例中,SortableStrings類型的Sort()方法其實是經過函數sort.Sort()來對接收者的值進行排序的。sort.Sort()函數接受一個類型爲sort.Interface的參數值,並利用這個值的方法Len、Less和Swap來修改其參數中的各個元素的位置以完成排序工做。再來看SortableStrings類型,雖然它實現了接口類型sort.Interface中聲明的所有方法,可是這些方法都是值方法,這使得在這些方法中對接收者值的改變並不會影響到它的源值。由於,它們只是改變了源值的某個複製品。這就是Sort方法失效的真正緣由。當咱們把SortableStrings類型的方法Len、Less和Swap的接收者類型都改成*SortableStrings以後,這個問題就會獲得解決。可是,這時的SortableStrings類型就已經再也不是接口類型sort.Interface的實現了。
嘰裏咕嚕地說了這麼多,就是說自定義類型在實現接口sort.Interface時的方法接收者都是值方法,非指針方法。
那麼什麼是值方法?什麼是指針方法?
好像我又挖了一個坑,再解釋一下吧 :)
type SortableStrings [3]string
func (s SortableStrings) Sort() { // 接收者s的類型爲值類型,因此這樣的方法稱之爲值方法
//......略
}
func (s *SortableStrings) Sort() { // 接收者s的類型爲指針類型,因此這樣的方法稱之爲指針方法
//......略
}
把上面的自定義類型SortableStrings的接口實現方法都修改成指針方法:
package main
import (
"fmt"
"sort"
)
type Sortable interface {
sort.Interface
Sort()
}
type SortableStrings [3]string
func (s *SortableStrings) Len() int {
return len(s)
}
func (s *SortableStrings) Less(i, j int) bool {
return s[i] < s[j]
}
func (s *SortableStrings) Swap(i, j int) {
s[i], s[j] = s[j], s[i]
}
func (s *SortableStrings) Sort() {
sort.Sort(s)
}
func main() {
_, ok := interface{}(SortableStrings{}).(sort.Interface)
fmt.Println(ok)
_, ok2 := interface{}(SortableStrings{}).(Sortable)
fmt.Println(ok2)
ss := SortableStrings{"2", "3", "1"}
ss.Sort()
fmt.Printf("Sortable strings: %v\n", ss)
}
再執行一下,看看結果變了沒有?
Sortable strings: [1 2 3]
真的變了,說明排序已成功,但上面的兩個斷言都變成了false。
那什麼地方用值方法?什麼地方用指針方法?
再摘抄書上的原話:
實際上,這也是一個在值方法和指針方法之間作選擇的問題。這裏有兩條很重要的規則。
在某個自定義數據類型的值上,只可以用與這個數據類型相關聯的值方法,而在指向這個值的指針值上,卻可以調用與其數據類型關聯的值方法和指針方法。從另外一個角度講,自定義數據類型的方法集合中僅包含了與它關聯的全部值方法,而與它相對應的指針類型的方法集合中卻包含了與它關聯的全部值方法和全部指針方法。
在指針方法中必定可以改變接收者的值,而在值方法中,對接收者的值的改變對於該方法以外通常是無效的。這是由於,以接收者標識符表明的接收者的值實際上也是當前方法所屬的數據類型的當前值的一個複製品。對於值方法來講,因爲這個接收者的值就是一個當前值的複製品,因此對它的改變並不會影響到當前值。而對於這個指針方法來講,這個接收者的值則是一個當前值的指針的×××。所以,依據這個指針來對當前值作變動,就等於直接對該值進行了改變。
嘰裏咕嚕地說了這麼多,這是什麼意思呢?
我曾經在另外的博客中說過,高手就是可以把別人繞暈,把本身繞暈,而後再繞出來的人 :)
先說第2條,用例子說吧:
package main
import (
"fmt"
)
type MyString struct { // 自定義數據類型
name string
}
func (m MyString) modify() { // 經過值方法修改接受者的值
m.name = "omgs"
}
func (m *MyString) update() { // 經過指針方法修改接受者的值
m.name = "pwm"
}
func main() {
var s = MyString{"ccq"}
fmt.Println(s.name) // 初始化name的值爲ccq
s.modify() // 調用值方法修改name爲omgs
fmt.Println(s.name) // 打印發現值依舊是ccq,說明modify()並無修改爲功
s.update() // 調用指針方法修改name爲pwm
fmt.Println(s.name) // 打印發現值爲pwm,說明update()成功修改了name值
}
再說第1條,坦率地講我是屬於被繞進去一直出不來的那種,呵呵呵,就上面例子來講,對照着做者原話去扣:
在某個自定義數據類型的值上:即s
只可以調用與這個數據類型相關聯的值方法:與自定義數據類型相關聯的值方法是modify(),按這個意思,s只可以調用modify()方法。惋惜的很,s也能調用update()方法,同時執行成功。
在指向這個值的指針值上,卻可以調用與其數據類型關聯的值方法和指針方法:即(&s)能夠調用modify(),也能調用update(),經測試驗證(&s).modify()和(&s).update()都能調用。這個沒毛病!
按照內事不知問度孃的邏輯,百度了一下,結果發現不少都是這樣寫的:
在某個自定義數據類型的值上,只可以調用與這個數據類型相關聯的值方法,而在指向這個值的指針值上,卻可以調用與其數據類型關聯的值方法和指針方法。雖然自定義數據類型的方法集合中不包含與它關聯的指針類型,可是咱們仍可以經過這個類型的值調用它的指針方法,這裏須要使用取地址符&。前半句同樣,後面的解釋不一樣,但解釋不通的地方在於最後是否必須用&符的地方。
其實這個地方不用糾結,這裏只須要理解自定義數據類型的方法集合中是否包含指針方法便可,這點在前面已經驗證過了:
package main
import "fmt"
type Sortable interface {
Sort()
}
type SortableStrings [3]string // 自定義一個數據類型SortableString
func (s SortableStrings) Sort() {
}
func main() {
_, ok2 := interface{}(SortableStrings{}).(Sortable)
fmt.Println(ok2) // 這裏打印是的true
}
這裏打印true說明值方法是SortableString關聯方法集合中的方法,但把值方法Sort()換成指針方法,就會打印false,因此書中做者認爲指針方法不是SortableString關聯方法集合中的方法。