Go 語言導出包解密:外部訪問你的類型和值

一、概述

Go 語言實現封裝和可見性控制的主要方式是通過標識符的首字母大小寫來決定其是否可被其他包訪問。

標識符如果首字母大寫就可以被外部包訪問 (導出), 可以選擇性地控制包內代碼的對外可見性。

本文介紹如何導出包中的標識符, 讓外部包可以訪問包內定義的類型、變量、常量、函數、導出類型注意事項。

主要內容包括

  • 導出標識符的規則

  • 導出類型的注意事項

  • 導出結構體字段的方法

  • 導出函數的注意事項

  • 內部包的使用

  • 示例: 導出圖形數學包中的類型與函數

通過本文可瞭解 Go 語言的可見性控制機制, 掌握包內代碼導出的方法, 在編寫 Go 程序時合理控制代碼的對外接口。

二、導出標識符的規則

Go 語言中, 如果標識符首字母大寫, 則可以被同一個包內的其他文件訪問, 也可以被其他包訪問。稱爲導出 (export) 標識符。

例如在 util 包中定義

package util
var PrivateVariable int 
type PublicType struct{}
func privateFunction(){}
func PublicFunction(){}

其他包導入 util 後

import "util"
util.PublicFunction() // ok
util.PublicType // ok
util.privateFunction() // error
util.PrivateVariable // error

由此可見, 只有 PublicFunction 和 PublicType 可被外部訪問, 私有標識符不能訪問。

結論就是, Go 語言通過首字母大小寫來決定標識符的對外可見性, 這是 Go 語言中實現封裝和信息隱藏的主要方式。

三、導出類型的注意事項

導出自定義的類型時, 需要注意

1、類型名要大寫, 如 PublicType struct{}。

2、類型內嵌的其他類型也應該是可導出的

type publicType1 struct{} 
type PublicType2 struct {
  publicType1 // 嵌套的類型也應該可導出
}

3、結構體字段如果要導出, 則必須是可導出類型, 並且大寫首字母。

type MyStruct struct {
  PublicField1 int
  publicField2 int // error,小寫不能導出
}

四、導出結構體字段的方法

導出一個結構體類型時, 外部代碼可以訪問到該結構體類型本身, 但是默認情況下無法訪問該類型的字段。如果需要導出結構體字段, 必須將字段首字母設置爲大寫。

例如

package data
type User struct {
  ID int
  Name string 
}
type Product struct {
  id int //小寫,不能被外部訪問
  Name string
}

其他包中

import "data"
var u data.User 
u.ID = 1 // OK
u.Name = "John" // OK
var p data.Product
p.id = 1  //錯誤,不能訪問
p.Name = "Apples" // OK

結構體類型導出後, 默認情況下外部代碼只能訪問類型本身, 不能訪問非導出字段。

若是想讓外部代碼訪問結構體字段, 必須將字段首字母大小寫切換爲大寫即可。

五、導出函數的注意事項

導出函數也有以下注意事項:

1、函數名需要大寫首字母。

2、函數參數和返回值類型必須是可導出的。

func ExportFunc(param PublicType) PublicType {
  //...
}

3、若是函數返回多個值, 所有返回值類型都必須可導出:

func ExportMulti(int) (PublicType, error) {
  // ...
}

六、內部包

若是希望包內代碼完全不被其他包訪問, 可以將包命名以 _ 下劃線開頭, 稱爲內部包。

例如:

import _ "myapp/internal/tools"

則 tools 包中的代碼完全不可被外部訪問。

這通常用於一些程序內部使用的包, 外部不需要訪問的場景。

七、示例: 導出圖形數學包中的類型與函數

下面創建一個圖形數學相關的 math 包, 其中包含計算周長和麪積的函數, 示例導出該包中的類型與函數。

package math
import "math"
// Point 是二維座標類型
type Point struct {
  X, Y float64
}
// Circle 是圓類型
type Circle struct {
  Center Point //圓心
  Radius float64 //半徑
}
// Perimeter計算圖形的周長
func Perimeter(graph Object) float64 {
  switch g := graph.(type) {
    case Circle:
      return 2 * math.Pi * g.Radius
    case Rectangle:
      return 2*g.Width + 2*g.Height
  }
  return 0
}
// Area計算圖形的面積
func Area(graph Object) float64 {
  switch g := graph.(type) {
    case Circle:
      return math.Pi * g.Radius * g.Radius
    case Rectangle:
      return g.Width * g.Height
  }
  return 0
}

在其他包中可以如下使用:

import "math"
circle := math.Circle{
  Center: math.Point{X: 0, Y:0},
  Radius: 5, 
}
math.Perimeter(circle) //可以調用
math.Area(circle)

由此可見, 將包內的類型名和函數名首字母大寫, 可以選擇性地導出這些標識符, 讓外部包根據需要訪問使用。

如果類型名和函數名首字母小寫, 則無法被外部訪問。這便實現了良好的封裝性。

八、總結

Go 語言通過標識符首字母大小寫來控制對外可訪問性, 實現了封裝和信息隱藏:

  • 大寫首字母標識符可被外部包訪問 (導出)

  • 小寫首字母標識符不能被外部包訪問 (非導出)

要導出類型需要將類型名弄成大寫開頭, 嵌套類型和結構體字段也要導出類型。

導出函數同樣要將函數名大寫, 參數和返回值也必須是可導出類型。

合理地導出包內代碼, 可以讓外部有選擇地訪問和使用包提供的功能, 是一個好的 Go 語言編程實踐。

本文由 Readfog 進行 AMP 轉碼,版權歸原作者所有。
來源https://mp.weixin.qq.com/s/ghqkOsn9BLVxnbQFdgf5wA