Go 語言不支持面向?qū)ο缶幊陶Z言中那樣的構(gòu)造子方法,但是可以很容易的在 Go 中實現(xiàn) “構(gòu)造子工廠”方法。為了方便通常會為類型定義一個工廠,按慣例,工廠的名字以 new 或 New 開頭。假設(shè)定義了如下的 File 結(jié)構(gòu)體類型:
type File struct {
fd int // 文件描述符
name string // 文件名
}
下面是這個結(jié)構(gòu)體類型對應(yīng)的工廠方法,它返回一個指向結(jié)構(gòu)體實例的指針:
func NewFile(fd int, name string) *File {
if fd < 0 {
return nil
}
return &File{fd, name}
}
然后這樣調(diào)用它:
f := NewFile(10, "./test.txt")
在 Go 語言中常常像上面這樣在工廠方法里使用初始化來簡便的實現(xiàn)構(gòu)造函數(shù)。
如果 File 是一個結(jié)構(gòu)體類型,那么表達式 new(File) 和 &File{} 是等價的。
這可以和大多數(shù)面向?qū)ο缶幊陶Z言中笨拙的初始化方式做個比較:File f = new File(...)。
我們可以說是工廠實例化了類型的一個對象,就像在基于類的OO語言中那樣。
如果想知道結(jié)構(gòu)體類型T的一個實例占用了多少內(nèi)存,可以使用:size := unsafe.Sizeof(T{})。
如何強制使用工廠方法
通過應(yīng)用可見性規(guī)則參考4.2.1節(jié)、9.5 節(jié)就可以禁止使用 new 函數(shù),強制用戶使用工廠方法,從而使類型變成私有的,就像在面向?qū)ο笳Z言中那樣。
type matrix struct {
...
}
func NewMatrix(params) *matrix {
m := new(matrix) // 初始化 m
return m
}
在其他包里使用工廠方法:
package main
import "matrix"
...
wrong := new(matrix.matrix) // 編譯失?。╩atrix 是私有的)
right := matrix.NewMatrix(...) // 實例化 matrix 的唯一方式
new 和 make 這兩個內(nèi)置函數(shù)已經(jīng)在第 7.2.4 節(jié)通過切片的例子說明過一次。
現(xiàn)在為止我們已經(jīng)見到了可以使用 make() 的三種類型中的其中兩個:
slices / maps / channels(見第 14 章)
下面的例子說明了在映射上使用 new 和 make 的區(qū)別以及可能發(fā)生的錯誤:
示例 10.4 new_make.go(不能編譯)
package main
type Foo map[string]string
type Bar struct {
thingOne string
thingTwo int
}
func main() {
// OK
y := new(Bar)
(*y).thingOne = "hello"
(*y).thingTwo = 1
// NOT OK
z := make(Bar) // 編譯錯誤:cannot make type Bar
(*z).thingOne = "hello"
(*z).thingTwo = 1
// OK
x := make(Foo)
x["x"] = "goodbye"
x["y"] = "world"
// NOT OK
u := new(Foo)
(*u)["x"] = "goodbye" // 運行時錯誤!! panic: assignment to entry in nil map
(*u)["y"] = "world"
}
試圖 make() 一個結(jié)構(gòu)體變量,會引發(fā)一個編譯錯誤,這還不是太糟糕,但是 new() 一個映射并試圖使用數(shù)據(jù)填充它,將會引發(fā)運行時錯誤! 因為 new(Foo) 返回的是一個指向 nil 的指針,它尚未被分配內(nèi)存。所以在使用 map 時要特別謹(jǐn)慎。