从 Kotlin/Java 迁移 Go:对照表 + 上路
你走完了整本书。从第 1 章「学 Go 是一次卸载」,到卷 III 把接口这颗最难啃的骨头拆开,到卷 V 拿到并发那份大礼——现在把这一切浓缩成一页可以随时翻回来查的东西:一张迁移对照表、你最容易犯的十个错、地道 Go 的心态、以及接下来该读什么、写什么。这一章是给你上路用的。
一张对照表:你的 Kotlin/Java 反射,在 Go 里对应什么
| 你在 Kotlin/Java 里 | Go 里 | 见 |
|---|---|---|
class B : A 继承 | 嵌入(组合)+ 接口做多态 | 12 |
interface + class X : I | 结构化接口,隐式满足,无 implements | 09 |
try / catch / throw | if err != nil,错误是返回值 | 14 |
catch (E e) { e.field } | errors.As(err, &target) | 15 |
构造函数 / init | 零值 + NewXxx() 约定函数 | 04 |
data class | struct(== 和复制免费,其余手写 / tag) | 06 |
x?.y 空安全 | 零值 + comma-ok + 显式判 nil | 04 |
sealed class + 穷尽 when | 接口 + 类型 switch + default(不穷尽) | 11 |
| 扩展函数 | 只能给本包类型加方法 | 08 |
| 方法重载 / 默认参数 | 起不同名字 / 变长参数 / 选项 struct | 02 |
List / MutableList | 切片 []T(当心 append 别名) | 07 |
launch { } 协程 | go f() goroutine(无 suspend 染色) | 17 |
Channel / Flow | channel(更中心、更低层) | 18 |
CoroutineScope / Job 树 | context(手动传 ctx) | 20 |
synchronized / Atomic | sync.Mutex / sync/atomic | 21 |
泛型 + in/out 型变 | 类型参数 + 约束(无型变) | 22 |
| JUnit / Gradle / JVM 部署 | 内建 go test / 一个静态二进制 | 23 |
你最容易犯的十个错
这些几乎是每个从 Kotlin/Java 来的人都会犯一遍的。提前知道,能省你很多抓头的时间:
- 传了值想改它、忘了用指针 —— 改的是副本,原对象没变。(第 5 章)看到「怎么没改成」先想指针。
- 值类型不满足接口,因为方法是指针接收者 —— 那句
does not implement (pointer receiver),加个&。(第 10 章) - append 到子切片改了父切片 —— 共享底层数组的别名坑。(第 7 章)
- 返回了一个 nil 的具体指针当 error,上层
err != nil却成立 —— 永远return nil。(第 13 章) - 往 nil map 写 panic —— map 要先
make。(第 4、7 章) - main 提前返回,goroutine 没跑完 —— 要用 WaitGroup / channel 等。(第 17 章)
- goroutine 泄漏 —— 起了一个永远阻塞、不会退出的 goroutine。(第 19 章)
- 在循环里 defer —— 攒到函数结束才执行,耗尽句柄。(第 16 章)
- 把 panic/recover 当 try/catch 用 —— 日常错误该 return error。(第 16 章)
- 用
==比较可能被包装过的错误 —— 要用errors.Is。(第 15 章)
如果把这一整本书的「怎么写才地道」压成几条心法:
1. 清晰胜过聪明。把代码写得笨一点、直一点、让下一个人一眼看懂,别炫技。
2. 接口要小、按需浮现、写在消费者一侧。别提前规划一堆大接口。
3. 错误是值,显式处理,别藏。那些 if err != nil 是特性不是负担。
4. 用组合,不用继承;用通信,优先于共享内存。
5. 先具体、后接口、真重复了才泛型。抵制过度抽象的冲动。
6. 每个 goroutine 都要能答出「它怎么退出」。
这六条背后是同一件事:Go 的一切设计,都在为「一个大团队、很长时间、还能读懂并改动这份代码」服务。写 Go 时,脑子里放着那个未来的读者,你就不会走偏。
标准库巡礼:先认识这几个包
Go 的标准库又大又好用,很多在别的语言要靠第三方的,它自带。开头最该熟悉这几个:
| 包 | 干嘛的 |
|---|---|
fmt | 格式化输出 / 字符串(Println / Sprintf / Errorf) |
errors | 错误处理(Is / As / Join) |
strings / strconv | 字符串操作 / 与数字互转 |
net/http | 内建的 HTTP 客户端和服务器 —— 几行就起一个 web 服务 |
encoding/json | JSON 编解码(配 struct tag,第 6 章) |
io / bufio / os | IO、文件、Reader/Writer 接口 |
context | 取消与超时传播(第 20 章) |
sync / time | 同步原语 / 时间与定时器 |
slices / maps | 泛型的切片 / map 工具(1.21+,终于有了) |
testing | 测试 / 基准 / fuzzing(第 23 章) |
net/http 尤其值得一试 —— Go 的标准库自带一个生产级可用的 HTTP 服务器,十几行代码就能起一个真能对外服务的 web 服务,不用任何框架。这也是 Go 在后端如此流行的原因之一:写服务的基础设施,标准库里就有。
接下来读什么、写什么
这本书是你的「换脑」之旅 —— 它专攻「Go 和你熟的语言差在哪、为什么」。走完它,你已经越过了最难的那道坎(不是语法难,是直觉的重装难)。接下来把地基夯实、把手练熟:
- A Tour of Go(go.dev/tour):官方交互式入门,把语法在浏览器里全过一遍手,补齐本书为聚焦「差异」而略过的语法细节。
- Effective Go(go.dev/doc/effective_go):官方的「怎么写地道 Go」,本书心法的权威详版,务必读。
- Go Proverbs(go-proverbs.github.io):本书引过好几条的那些谚语,Rob Pike 有个 15 分钟的演讲,值得看一遍,它们是 Go 的价值观浓缩。
- 《The Go Programming Language》(Donovan & Kernighan,人称 GOPL):Go 界的「K&R」,最权威系统的一本,当工具书。
- Go by Example(gobyexample.com):按主题的小例子速查,忘了某个用法时来这查最快。
但比读什么更重要的是 —— 写。找一个真实的小东西用 Go 写一遍:一个命令行工具、一个 HTTP API、一个把某个脚本重写成 Go 的小项目。你会在写的过程中,把这本书里所有「原来如此」的瞬间,变成肌肉记忆。Go 是一门为「写」而生的语言,它的好,得写出来才知道。
这本书从头到尾在说一件事:Go 的力量来自它删掉的东西,不是它加上的东西。没有继承、没有异常、没有构造函数、没有那么多花样 —— 每一处留白,都是为了让一份代码在一个大团队、很长的时间里,还能被读懂、被信任、被安心地改动。
你带着十年的 Kotlin/Java 直觉来。走到这里,你已经把其中一部分(继承的层级、异常的抛接、万物皆引用、处处的语法糖)放下了,也把另一部分(函数、抽象、组合、并发的思维)用 Go 的方式重新接了回来。这就是「卸载」的意义 —— 你没有变笨,你变得能在两种性格的语言之间自由切换,并且更清楚地知道,每一个「语言特性」到底是在帮你,还是只是在讨好写代码时的你。
路已经在脚下了。去写点什么吧。
这一章的一句话
把整本书压成一页:一张 Kotlin→Go 对照表、十个必犯的错、六条地道 Go 心法、一份标准库和进阶清单——但真正让这一切落地的,是你亲手用 Go 写一个真实的小东西。Go 的好,写出来才知道。
《留白》到此结束。谢谢你读到这里 —— 从「学 Go 是一次卸载」出发,你已经把一门做减法的语言,装进了脑子。现在,轮到你去用它了。