From 790736c0873ab8d0a67f5cc30b38308f9ebb53ca Mon Sep 17 00:00:00 2001 From: chai2010 Date: Sat, 2 Jan 2016 21:53:14 +0800 Subject: [PATCH] =?UTF-8?q?ch6-2:=20=E5=AD=90=E7=AB=A0=E8=8A=82=E6=94=BE?= =?UTF-8?q?=E5=88=B0=E7=8B=AC=E7=AB=8B=E6=96=87=E4=BB=B6?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- ch6/ch6-02-1.md | 66 +++++++++++++++++++++++++++++++++++++++++++++++++ ch6/ch6-02.md | 66 +------------------------------------------------ 2 files changed, 67 insertions(+), 65 deletions(-) create mode 100644 ch6/ch6-02-1.md diff --git a/ch6/ch6-02-1.md b/ch6/ch6-02-1.md new file mode 100644 index 0000000..38a58d4 --- /dev/null +++ b/ch6/ch6-02-1.md @@ -0,0 +1,66 @@ +### 6.2.1. Nil也是一個合法的接收器類型 + +就像一些函數允許nil指針作爲參數一樣,方法理論上也可以用nil指針作爲其接收器,尤其當nil對於對象來説是合法的零值時,比如map或者slice。在下面的簡單int鏈表的例子里,nil代表的是空鏈表: + +```go +// An IntList is a linked list of integers. +// A nil *IntList represents the empty list. +type IntList struct { + Value int + Tail *IntList +} +// Sum returns the sum of the list elements. +func (list *IntList) Sum() int { + if list == nil { + return 0 + } + return list.Value + list.Tail.Sum() +} +``` + +當你定義一個允許nil作爲接收器值的方法的類型時,在類型前面的註釋中指出nil變量代表的意義是很有必要的,就像我們上面例子里做的這樣。 + +下面是net/url包里Values類型定義的一部分。 + +```go +net/url +package url + +// Values maps a string key to a list of values. +type Values map[string][]string +// Get returns the first value associated with the given key, +// or "" if there are none. +func (v Values) Get(key string) string { + if vs := v[key]; len(vs) > 0 { + return vs[0] + } + return "" +} +// Add adds the value to key. +// It appends to any existing values associated with key. +func (v Values) Add(key, value string) { + v[key] = append(v[key], value) +} +``` + +這個定義向外部暴露了一個map的類型的變量,併且提供了一些能夠簡單操作這個map的方法。這個map的value字段是一個string的slice,所以這個Values是一個多維map。客戶端使用這個變量的時候可以使用map固有的一些操作(make,切片,m[key]等等),也可以使用這里提供的操作方法,或者兩者併用,都是可以的: + +```go +gopl.io/ch6/urlvalues +m := url.Values{"lang": {"en"}} // direct construction +m.Add("item", "1") +m.Add("item", "2") + +fmt.Println(m.Get("lang")) // "en" +fmt.Println(m.Get("q")) // "" +fmt.Println(m.Get("item")) // "1" (first value) +fmt.Println(m["item"]) // "[1 2]" (direct map access) + +m = nil +fmt.Println(m.Get("item")) // "" +m.Add("item", "3") // panic: assignment to entry in nil map +``` + +對Get的最後一次調用中,nil接收器的行爲卽是一個空map的行爲。我們可以等價地將這個操作寫成Value(nil).Get("item"),但是如果你直接寫nil.Get("item")的話是無法通過編譯的,因爲nil的字面量編譯器無法判斷其準備類型。所以相比之下,最後的那行m.Add的調用就會産生一個panic,因爲他嚐試更新一個空map。 + +由於url.Values是一個map類型,併且間接引用了其key/value對,因此url.Values.Add對這個map里的元素做任何的更新、刪除操作對調用方都是可見的。實際上,就像在普通函數中一樣,雖然可以通過引用來操作內部值,但在方法想要脩改引用本身是不會影響原始值的,比如把他置爲nil,或者讓這個引用指向了其它的對象,調用方都不會受影響。(譯註:因爲傳入的是存儲了內存地址的變量,你改變這個變量是影響不了原始的變量的,想想C語言,是差不多的) diff --git a/ch6/ch6-02.md b/ch6/ch6-02.md index 0aa9c70..967fc36 100644 --- a/ch6/ch6-02.md +++ b/ch6/ch6-02.md @@ -90,68 +90,4 @@ pptr.Distance(q) // implicit (*pptr) 1.不管你的method的receiver是指針類型還是非指針類型,都是可以通過指針/非指針類型進行調用的,編譯器會幫你做類型轉換 2.在聲明一個method的receiver該是指針還是非指針類型時,你需要考慮兩方面的內部,第一方面是這個對象本身是不是特别大,如果聲明爲非指針變量時,調用會産生一次拷貝;第二方面是如果你用指針類型作爲receiver,那麽你一定要註意,這種指針類型指向的始終是一塊內存地址,就算你對其進行了拷貝。熟悉C或者C艹的人這里應該很快能明白。 -###6.2.1. Nil也是一個合法的接收器類型 -就像一些函數允許nil指針作爲參數一樣,方法理論上也可以用nil指針作爲其接收器,尤其當nil對於對象來説是合法的零值時,比如map或者slice。在下面的簡單int鏈表的例子里,nil代表的是空鏈表: - -```go -// An IntList is a linked list of integers. -// A nil *IntList represents the empty list. -type IntList struct { - Value int - Tail *IntList -} -// Sum returns the sum of the list elements. -func (list *IntList) Sum() int { - if list == nil { - return 0 - } - return list.Value + list.Tail.Sum() -} -``` - -當你定義一個允許nil作爲接收器值的方法的類型時,在類型前面的註釋中指出nil變量代表的意義是很有必要的,就像我們上面例子里做的這樣。 - -下面是net/url包里Values類型定義的一部分。 - -```go -net/url -package url - -// Values maps a string key to a list of values. -type Values map[string][]string -// Get returns the first value associated with the given key, -// or "" if there are none. -func (v Values) Get(key string) string { - if vs := v[key]; len(vs) > 0 { - return vs[0] - } - return "" -} -// Add adds the value to key. -// It appends to any existing values associated with key. -func (v Values) Add(key, value string) { - v[key] = append(v[key], value) -} -``` - -這個定義向外部暴露了一個map的類型的變量,併且提供了一些能夠簡單操作這個map的方法。這個map的value字段是一個string的slice,所以這個Values是一個多維map。客戶端使用這個變量的時候可以使用map固有的一些操作(make,切片,m[key]等等),也可以使用這里提供的操作方法,或者兩者併用,都是可以的: - -```go -gopl.io/ch6/urlvalues -m := url.Values{"lang": {"en"}} // direct construction -m.Add("item", "1") -m.Add("item", "2") - -fmt.Println(m.Get("lang")) // "en" -fmt.Println(m.Get("q")) // "" -fmt.Println(m.Get("item")) // "1" (first value) -fmt.Println(m["item"]) // "[1 2]" (direct map access) - -m = nil -fmt.Println(m.Get("item")) // "" -m.Add("item", "3") // panic: assignment to entry in nil map -``` - -對Get的最後一次調用中,nil接收器的行爲卽是一個空map的行爲。我們可以等價地將這個操作寫成Value(nil).Get("item"),但是如果你直接寫nil.Get("item")的話是無法通過編譯的,因爲nil的字面量編譯器無法判斷其準備類型。所以相比之下,最後的那行m.Add的調用就會産生一個panic,因爲他嚐試更新一個空map。 - -由於url.Values是一個map類型,併且間接引用了其key/value對,因此url.Values.Add對這個map里的元素做任何的更新、刪除操作對調用方都是可見的。實際上,就像在普通函數中一樣,雖然可以通過引用來操作內部值,但在方法想要脩改引用本身是不會影響原始值的,比如把他置爲nil,或者讓這個引用指向了其它的對象,調用方都不會受影響。(譯註:因爲傳入的是存儲了內存地址的變量,你改變這個變量是影響不了原始的變量的,想想C語言,是差不多的) +{% include "./ch6-02-1.md" %}