卷 VI · 工程CH 24深度 24/24

从 Kotlin/Java 迁移 Go:对照表 + 上路

本章卸下最后一点「披着 Go 皮写 Kotlin」的惯性

你走完了整本书。从第 1 章「学 Go 是一次卸载」,到卷 III 把接口这颗最难啃的骨头拆开,到卷 V 拿到并发那份大礼——现在把这一切浓缩成一页可以随时翻回来查的东西:一张迁移对照表、你最容易犯的十个错、地道 Go 的心态、以及接下来该读什么、写什么。这一章是给你上路用的。

迁移对照表常见错误地道 Go标准库上路

一张对照表:你的 Kotlin/Java 反射,在 Go 里对应什么

你在 Kotlin/Java 里Go 里
class B : A 继承嵌入(组合)+ 接口做多态12
interface + class X : I结构化接口,隐式满足,无 implements09
try / catch / throwif err != nil,错误是返回值14
catch (E e) { e.field }errors.As(err, &target)15
构造函数 / init零值 + NewXxx() 约定函数04
data classstruct(== 和复制免费,其余手写 / tag)06
x?.y 空安全零值 + comma-ok + 显式判 nil04
sealed class + 穷尽 when接口 + 类型 switch + default(不穷尽)11
扩展函数只能给本包类型加方法08
方法重载 / 默认参数起不同名字 / 变长参数 / 选项 struct02
List / MutableList切片 []T(当心 append 别名)07
launch { } 协程go f() goroutine(无 suspend 染色)17
Channel / Flowchannel(更中心、更低层)18
CoroutineScope / Jobcontext(手动传 ctx)20
synchronized / Atomicsync.Mutex / sync/atomic21
泛型 + in/out 型变类型参数 + 约束(无型变)22
JUnit / Gradle / JVM 部署内建 go test / 一个静态二进制23

你最容易犯的十个错

这些几乎是每个从 Kotlin/Java 来的人都会犯一遍的。提前知道,能省你很多抓头的时间:

  1. 传了值想改它、忘了用指针 —— 改的是副本,原对象没变。(第 5 章)看到「怎么没改成」先想指针。
  2. 值类型不满足接口,因为方法是指针接收者 —— 那句 does not implement (pointer receiver),加个 &。(第 10 章)
  3. append 到子切片改了父切片 —— 共享底层数组的别名坑。(第 7 章)
  4. 返回了一个 nil 的具体指针当 error,上层 err != nil 却成立 —— 永远 return nil。(第 13 章)
  5. 往 nil map 写 panic —— map 要先 make。(第 4、7 章)
  6. main 提前返回,goroutine 没跑完 —— 要用 WaitGroup / channel 等。(第 17 章)
  7. goroutine 泄漏 —— 起了一个永远阻塞、不会退出的 goroutine。(第 19 章)
  8. 在循环里 defer —— 攒到函数结束才执行,耗尽句柄。(第 16 章)
  9. 把 panic/recover 当 try/catch 用 —— 日常错误该 return error。(第 16 章)
  10. == 比较可能被包装过的错误 —— 要用 errors.Is。(第 15 章)
◆ 地道 Go 的心态,一句话版

如果把这一整本书的「怎么写才地道」压成几条心法:

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/jsonJSON 编解码(配 struct tag,第 6 章)
io / bufio / osIO、文件、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 是一次卸载」出发,你已经把一门做减法的语言,装进了脑子。现在,轮到你去用它了。