Import / Export(导入与导出)
Go 使用 import 引用其他包,通过标识符首字符的大小写决定名称能否被其他包访问。它没有 public、private 或 export 关键字;可见性的基本边界是包,而不是源文件。
导入并使用包
多个导入通常写成分组形式:
package main
import (
"fmt"
"strings"
)
func main() {
message := strings.ToUpper("hello, go")
fmt.Println(message)
}
运行后输出:
HELLO, GO
导入路径定位包,代码默认使用目标包在 package 子句中声明的名称。例如导入 "encoding/json" 后通常通过 json.Marshal 调用函数。
导出规则
一个名称满足以下条件时才是已导出的(exported):
- 名称的首字符是 Unicode 大写字母。
- 名称声明在包块中,或者它是字段名或方法名。
package profile
const DefaultRole = "reader" // 可被其他包访问
const maxNameBytes = 64 // 仅当前包可访问
type User struct {
Name string // 导出字段
password string // 未导出字段
}
func NewUser(name string) User { // 导出函数
return User{Name: name}
}
func normalizeName(name string) string { // 未导出函数
return name
}
User、Name 和 NewUser 可以从其他包访问,maxNameBytes、password 和 normalizeName 只能在 profile 包内部使用。同一个包即使分散在多个文件中,也可以访问彼此未导出的包级名称。
大写表示可访问,不等于 API 已经设计完善。导出的名称会扩大兼容性和维护范围,应只公开调用方真正需要的能力。
导入名称与别名
当包名冲突或默认名称不够清晰时,可以声明别名:
package main
import (
"fmt"
cryptorand "crypto/rand"
mathrand "math/rand"
)
func main() {
buffer := make([]byte, 4)
_, err := cryptorand.Read(buffer)
if err != nil {
fmt.Println("read random bytes:", err)
return
}
fmt.Println("secure bytes:", buffer)
fmt.Println("pseudo-random number:", mathrand.Intn(10))
}
别名只在当前文件生效。不要为每个导入随意起别名;默认包名通常更容易被 Go 开发者识别。
特殊导入形式
空白导入
使用空白标识符 _ 导入包,表示只需要该包的初始化副作用:
import _ "example.com/database-driver"
这种形式常见于数据库驱动或插件注册。它隐藏了直接调用关系,应在确有注册机制需要时使用,并通过注释或包文档说明副作用。
点导入
点导入会让目标包的已导出名称直接进入当前文件作用域:
import . "fmt"
此后可以直接写 Println,但名称来源变得不清晰,也更容易冲突。除少数测试或框架约定外,普通业务代码应避免点导入。
internal 包
目录名 internal 提供额外的导入限制。位于 internal 目录下的包,只能被以该 internal 父目录为根的目录树中的代码导入:
example.com/shop/
├── internal/token/
├── cmd/api/
└── tools/
这里 cmd/api 和 tools 可以导入 example.com/shop/internal/token,模块目录树之外的代码不可以。这是 Go 工具链实施的导入边界,与名称是否大写无关。
文件级导入与未使用检查
导入声明的作用域仅覆盖当前文件。下面的 fmt 即使在同包另一个文件中已导入,也不能直接使用:
package report
import "fmt"
func Print(title string) {
fmt.Println(title)
}
每个非空白导入都必须在当前文件中使用,否则编译器会报错。开发过程中不应长期用 _ 伪装未使用导入;应删除无用依赖,让工具在真正需要时重新加入。
与 TypeScript 的区别
| 对比项 | Go | TypeScript |
|---|---|---|
| 导入单位 | 包 | 模块及其导出成员 |
| 导出语法 | 包级名称、字段或方法以大写字母开头 | 使用 export 关键字 |
| 私有边界 | 未导出名称可被同包所有文件访问 | 未导出名称通常只在当前模块内访问 |
| 导入检查 | 当前文件的普通导入必须使用 | 取决于编译器和 lint 配置 |
Go 的大小写规则既影响命名,也定义公开 API。重命名首字母可能直接改变外部调用方能否编译。
依赖版本、模块路径和 go.mod 的完整规则见 Module Manager(模块管理)。
常见错误
- 导入完整路径后,仍用路径最后一段猜测包名;应以目标包声明为准。
- 试图从其他包访问小写开头的名称;未导出名称没有跨包访问入口。
- 为解决未使用导入而改成
_;空白导入只用于明确需要初始化副作用的场景。 - 大量使用点导入,导致读者无法判断名称来源。
- 认为导出字段仍然是“只读”的;大写字段通常可被其他包直接读取和修改。
小结
import引用包,多个导入通常使用分组写法。- 包级名称、字段和方法以大写字母开头时可导出,小写开头时仅包内可见。
- 导入别名用于解决真实冲突或提高辨识度,且只在当前文件有效。
_导入只保留初始化副作用,点导入应谨慎使用。internal目录可以在名称可见性之外进一步限制包的导入范围。