Go基礎系列:struct的匯出和暴露問題
struct的匯出和暴露問題
關於struct的匯出
struct的屬性是否被匯出,也遵循大小寫的原則:首字母大寫的被匯出,首字母小寫的不被匯出。
所以,如果struct名稱首字母是小寫的,這個struct不會被匯出。連同它裡面的欄位也不會匯出,即使有首字母大寫的欄位名。
如果struct名稱首字母大寫,則struct會被匯出,但只會匯出它內部首字母大寫的欄位,那些小寫首字母的欄位不會被匯出。
也就是說,struct的匯出情況是混合的。
但並非絕對如此,如果struct嵌套了,那麼即使被巢狀在內部的struct名稱首字母小寫,也能訪問到它裡面首字母大寫的欄位。
例如:
type animal struct{ name string Speak string } type Horse struct { animal sound string }
Horse中巢狀的animal是小寫字母開頭的,但Horse是能被匯出的,所以能在其它包中使用Horse struct,其他包也能訪問到animal中的Speak屬性。
很多時候,Horse這個名字是不安全的,因為這表示匯出Horse這個struct給其他包,也就是將Horse給暴露出去了,外界可以直接開啟Horse這個"黑匣子"。
但如果不將Horse匯出,如何能在其它包構建出Horse例項?見下文。
不要暴露struct
很多時候,不應該將某包(如包abc)中的struct(如animal)直接暴露給其它包,暴露意味著打開了那個"黑匣子",所以struct會以小寫字母開頭,不將其匯出。
這時在外界其它包中構建包abc的animal,就沒法直接通過以下幾種方式實現:
var xxx abc.animal
new(abc.animal)
&abc.animal{...}
abc.animal{...}
例如,下面的是錯誤的:
// abc/abc.go檔案內容: package abc type animal struct{ name string Speak string } // test.go內容: package main import "./abc" func main() { // 全都錯誤 var t1 abc.animal t2 := new(abc.animal) t3 := &abc.animal{} t4 := abc.animal{} }
那麼如何在外界構建隱藏起來的struct例項?這時可以在abc包中寫一個可匯出的函式,通過這個函式來構建struct例項。例如:
// abc/abc.go檔案內容:
package abc
type animal struct{
name string
Speak string
}
func NewAnimal() *animal{
a := new(animal)
return a
}
// test.go內容:
package main
import (
"fmt"
"./abc"
)
func main() {
t1 := abc.NewAnimal()
// t1.name = "haha" // 無法訪問name屬性
t1.Speak = "hhhh"
fmt.Println(t1.Speak)
}
上面的程式碼一切正常,在main包中可以通過NewAnimal()構建出abc包中未匯出的animal struct。注意,上面NewAnimal()中是使用new()函式構造例項的,它返回的是例項的指標,至於如何構造例項,完全可以根據自己的需求,但對於struct型別來說,一般都是使用指標的,也就是完全可以將new()通用化。
由於animal中的name欄位是不匯出的欄位,所以在外界即便是通過NewAnimal()構建出了animal例項,也無法訪問該例項的name屬性,所以沒法為name欄位賦值。換句話說,name屬性永遠是初始化的0值。
因此,為了讓構建例項時自定義name屬性,需要在構造方法NewAnimal()上指定設定給name屬性的引數。修改NewAnimal()函式:
func NewAnimal(name string) *animal{
a := new(animal)
a.name = name
return a
}
然後在其它包中構建animal例項:
t1 := abc.NewAnimal("longshuai")
雖然其它包中構建的animal例項已經具備了name屬性,但還是無法訪問該例項的name屬性。所以,在abc包中繼續寫一個可匯出的方法,該方法用於獲取例項的name屬性:
// abc/abc.go中新增:
func (a *animal) GetName() string {
return a.name
}
於是外界包中可以通過這個匯出的方法獲取例項的name屬性:
t1 := abc.NewAnimal("longshuai")
fmt.Println(abc.GetName())
實際上,上面NewAnimal()構造物件時,可以不用傳遞name引數,而是像GetName()一樣,寫一個專門的可匯出方法來設定例項的name屬性。改寫abc/abc.go中的程式碼:
func NewAnimal() *animal{
a := new(animal)
return a
}
func (a *animal) SetName(name string){
a.name = name
}
現在,abc/abc.go中的animal struct就完全對外隱藏了。