2015-06-26 73 views
3

我是Go新手,正在研究它的接口特性。當在Go中分配一個指向接口的指針時,與隱式指針解引用相混淆

下面是代碼:

package main 

import (
     "fmt" 
     "reflect" 
) 

type Integer int 

func (a Integer) Less(b Integer) bool { 
     return a < b 
} 

func (a *Integer) Add(b Integer) { 
     *a += b 
} 

type LessAdder interface { 
     Less(b Integer) bool 
     Add(b Integer) 
} 

var a Integer = 1 

var b LessAdder = &a 

func main() { 
     fmt.Println(reflect.TypeOf(b)) 
     fmt.Println(b.Less(2)) 
     b.Add(a) 
     fmt.Println(a) 

} 

,它會輸出如下:

*main.Integer 
true 
2 

好了,這工作得很好。

關鍵是: var b LessAdder = &a如何工作。指針自動解除引用是否在這裏發生,或者當b調用成員方法時?

輸出*main.Integer告訴我們b是指向Integer的指針,因此它是第二種情況。

那麼棘手的事情來了: 當我添加fmt.Pringln(*b)的代碼,編譯器配備了一個錯誤:

demo/demo1 
./demo1.go:31: invalid indirect of b (type LessAdder) 

它混淆了我。 由於b是一個指向Integer的指針類型,因此解引用它應該可以工作。但爲什麼不呢?

+1

除了什麼icza聲明:接口是**類型**在Go中,它們不是*語法糖或魔法包裝。接口類型具有與內建類型(如int32或用戶定義的結構)相同的狀態。 – Volker

回答

7

你的最後一句話:

"Since b is a pointer type to Integer , then dereferencing it should work."

停在那兒。 b不是指針類型的變量,因此你不能dereference它。

它是interface type的變量而言是示意性地在一對的值和類型(值,類型)的,保持&a作爲值和*Integer作爲類型(博客文章The Laws of Reflection,部分The representation of an interface)。

這是指針類型的變量聲明,*Integer

var ip *Integer 

,這是一個接口類型之一:

var intf LessAdder 

當你這樣做:

var b LessAdder = &a 

會發生什麼情況是創建了一個接口值(類型爲LessAdder)自動/隱式地保存值&a(和類型*Integer)。這是一個有效的操作,因爲&a(即*Integer)的類型實現了接口LessAddermethod set*Integer是接口LessAdder的超集(在這種情況下它們相同,接口類型的方法集是其接口) 。

現在當你調用b.Less(2),因爲Less()具有值接收器,該指針將被取消引用,所指向的值的副本將被製成,使用/作爲方法Less()的價值接收機通過。

fmt.Println(reflect.TypeOf(b))不說謊,但它會打印動態類型的bb的動態類型確實是*Integer,但靜態類型的bLessAdder,而靜態類型決定了您可以對值做什麼以及允許使用哪些運算符或方法。

+0

所以'b'是一種'Interface {&a,* Integer}',* Interface *是一種類型? – LeckieNi

+0

@LeckieNi示意圖,是的。但是會發生什麼是一個接口值是自動/隱式地保存配對'(&a,* Integer)'並且這個接口值被賦值給'b'。見編輯的答案。 – icza

+0

然後'b'調用'b.Less(2)',它實際上調用了'(&a).Less(2)'。這裏是自動取消引用的地方? – LeckieNi

1

LessAdder被聲明爲與方法LessAdd的接口。由於Add是用*Integer的接收器聲明的,所以*Integer可以是LessAdder; Integer不能。當您執行var b LessAdder = &a時,它是存儲在接口b中的指向a的指針。

自動間接發生在呼叫b.Less(2),因爲*Integer這兩種方法和Integer方法有助於*Integer的方法集。

不能使用*b因爲雖然b包含一個*Integer,其靜態類型是LessAdder,不*Integer。撇開接口的表示,LessAdder不是一個指針類型,而*b,如果它被允許的話,根本不會有可表達的類型。

您可以使用type assertion作爲Integer *再次訪問b; b.(*Integer)*Integer類型的表達式,並且*b.(*Integer)Integer。畢竟,如果b中的值不是*Integer,這兩種情況都會導致運行時恐慌。