Go36-3-代碼包
如果代碼太復雜,就可以把代碼寫在多個源碼文件裏。或者是其他代碼包中(這個後面講)
代碼示例
下面是程序的主體,但是其中調用了square()函數,這個函數並沒有聲明:
// Go36/article03/example01/demo.go package main import ( "flag" "fmt" ) var x int func init() { flag.IntVar(&x, "x", 0, "計算平方") } func main() { flag.Parse() res := square(x) fmt.Println(x, "的平方:", res) }
上面用到的square()函數被聲明在了另一個文件中:
// Go36/article03/example01/calc.go
package main
func square(x int) int {
return x * x
}
這裏只聲明了一個函數,可以在這個寫更多關於計算的函數,這樣在同個包裏都可以方便的調用這些函數使用。
上面的2個文件都要在同一個目錄下,並且需要被聲明為屬於同一個包。
執行代碼
因為示例中都聲明為main包,並且包裏也有一個main函數。所以存在一個命令源碼文件,這樣就可以直接運行起來:
PS H:\Go\src\Go36\article03\example01> go run demo.go calc.go -x 3 3 的平方: 9 PS H:\Go\src\Go36\article03\example01>
上面註意要把所有的文件都寫在命令裏。
還可以先構建代碼包,在執行:
PS H:\Go\src> go build Go36/article03/example01
PS H:\Go\src> .\example01.exe -x 4
4 的平方: 16
PS H:\Go\src>
把代碼拆分到多個包
先修改calc.go的路徑,並且做一些修改:
// Go36/article03/example02/lib/calc.go
package lib2
func Square(x int) int {
return x * x
}
這裏創建了一個子目錄,把文件放到了這個子目錄中,這樣使得它同命令源碼文件不在同一個目錄下了。
- 包名變成了lib2,這裏故意和目錄不是同一個名字
- 函數名的首字母變成了大寫
包名和目錄名不同
現在要使用上面的包。導入包的路徑應該是目錄的路徑名稱:
import (
"Go36/article03/example02/lib"
)
如果要構建或者安裝這個代碼包,使用的命令應該是下面這樣,還是用目錄名稱:
go install Go36/article03/example02/lib
並且命令成功後,pkg子目錄產生的歸檔文件也是目錄名稱:
pkg\windows_amd64\Go36\article03\example02\lib.a
但是最後調用的時候需要使用包名稱,命令源碼文件的代碼如下:
// Go36/article03/example02/demo.go
package main
import (
"flag"
"fmt"
"Go36/article03/example02/lib"
)
var x int
func init() {
flag.IntVar(&x, "x", 0, "計算平方")
}
func main() {
flag.Parse()
res := lib2.Square(x)
fmt.Println(x, "的平方:", res)
}
上面調用程序時使用的lib2.稱為限定符。
結論:導入路徑使用的是文件所在目錄的路徑。而調用程序時使用的限定符要與它聲明的包的名稱一致。
為了不在使用代碼包是產生困惑,應該讓聲明的包的名稱與其父目錄的目錄名稱一致。
訪問權限
在這裏把函數名稱的首字母改為大寫的原因是,名稱的首字母為大寫的程序實體才可以被當前包外的代碼引用,否則它就只能被當前包內的其他代碼引用。
這涉及了Go語言中對於程序實體訪問權限的規則。通過名稱的首字母的大小寫,就把訪問權限分為了包級私有和公開這兩種。對於包級私有,只有在包內部可以訪問。由於我們需要在main包裏調用lib包的函數,只能訪問到公開的部分,所以需要把函數的首字母大寫。
模塊級私有
上面的訪問權限都以包的級別進行劃分的。在Go 1.5及後續版本中,可以通過創建internal代碼包讓一些程序實體僅僅能被當前模塊中的其他代碼引用。這是第三種訪問權限:模塊級私有。
具體規則是,internal代碼包中聲明的公開程序實體僅能被該代碼包的直接父包及其子包中的代碼引用。當然,引用前需要先導入這個internal包。對於其他代碼包,導入該internal包都是非法的,無法通過編譯。
這裏的名稱必須是internal,示例如下:
// 父級目錄 Go36/article03/example03/demo.go
package main
import (
"flag"
"Go36/article03/example03/lib"
//"Go36/article03/example03/lib/internal" // 此行無法通過編譯。
)
var x int
func init() {
flag.IntVar(&x, "x", 0, "計算平方")
}
func main() {
flag.Parse()
lib.Cale(x)
//res := internal.Square(x)
//lib.Cale(res)
}
// 子級目錄 Go36/article03/example03/lib/demo_lib.go
package lib
import (
"fmt"
"Go36/article03/example03/lib/internal"
)
func Cale(x int) {
res := internal.Square(x)
fmt.Println(x, "的平方:", res)
}
// 孫子目錄 Go36/article03/example03/lib/internal/internal.go
package internal
func Square(x int) int {
return x * x
}
模塊級私有的internal包,僅能被直接父包及其子包中的代碼引用。上面如果要在父級裏調用孫級目錄的internal包,就是非法的:
PS H:\Go\src\Go36\article03\example03> go run demo.go -x 7
demo.go:6:2: use of internal package not allowed
PS H:\Go\src\Go36\article03\example03>
Go36-3-代碼包