一、概述
Go 语言实现封装和可见性控制的主要方式是通过标识符的首字母大小写来决定其是否可被其他包访问。
标识符如果首字母大写就可以被外部包访问(导出), 可以选择性地控制包内代码的对外可见性。
本文介绍如何导出包中的标识符,让外部包可以访问包内定义的类型、变量、常量、函数、导出类型注意事项。
主要内容包括
- 导出标识符的规则
- 导出类型的注意事项
- 导出结构体字段的方法
- 导出函数的注意事项
- 内部包的使用
- 示例:导出图形数学包中的类型与函数
通过本文可了解 Go 语言的可见性控制机制,掌握包内代码导出的方法,在编写 Go 程序时合理控制代码的对外接口。
二、导出标识符的规则
Go 语言中,如果标识符首字母大写,则可以被同一个包内的其他文件访问,也可以被其他包访问。称为导出(export)标识符。
例如在 util 包中定义
package util var PrivateVariable int type PublicType struct{} func privateFunction(){} func PublicFunction(){}
其他包导入 util 后
import "util" util.PublicFunction() // okutil.PublicType // ok util.privateFunction() // errorutil.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 // OKu.Name = "John" // OK var p data.Productp.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 语言编程实践。