Kotlin学习导航:从知识树走到项目与面试

一个 Android 页面同时碰到接口空值、列表转换、Java SDK、协程请求和 Compose 状态时,按语法关键词查资料很容易越查越散:?. 看懂了,仍不知道平台类型从哪里来;Flow 会收集了,页面退出后任务却还在跑。这八篇按"数据边界 → 行为与状态 → 平台边界 → 并发与交付"串起来读,每篇解决一个明确的问题,也能单独打开查阅。

这套文章以 Kotlin 2.4.21 为语言基线。协程、AndroidX、Compose、Gradle 和 KMP 依赖各有版本要求,实际项目仍要按自己的兼容组合配置。下图是总览;图片里的卡片是视觉索引,真正可点击的阅读入口在下面八个标题中。

如果刚开始系统学习,建议按 1 → 2 → 5 → 6 → 3 → 4 → 7 → 8 阅读:先把值、函数、分支和集合用顺手,再处理对象建模、JVM 边界、并发和工程验证。已经写过 Kotlin 的读者可从正在遇到的问题直接跳入对应篇。

01|类型与空安全:从接口数据到页面状态

**讲什么:**从 Byte 到 Double 的数值范围与显式转换,Boolean、Char、String 和无符号数的边界;再把 ?、?.、?:、!!、Java 平台类型与 Any、Unit、Nothing、数组放进一次接口数据进入页面的过程。文章还解释整数除法、金额单位、UTF-16 长度以及 Array<Int> 与 IntArray 的差别。

**目的:**读完应能在数据入口决定"缺失值是默认显示,还是中止流程",明确数值单位与合法范围,不靠 !! 消除编译错误,也不会把只读类型误当成业务校验。遇到 Java 返回值或 Android extra 时,先收紧类型再交给页面。

进入第 1 篇 →

02|变量、函数与扩展:把状态和行为写成契约

讲什么: val 与 var、默认参数和命名参数;函数类型、Lambda、高阶函数、尾随 Lambda;扩展函数、扩展属性和五个作用域函数;inline、reified 与返回标签。Kotlin 2.4 的 Context Parameters 也放在这一篇,讨论何时值得让一段调用链共享依赖。

**目的:**读完应能从签名看出谁提供数据、谁交出行为、谁持有回调。写 Android 监听器时会检查 Lambda 是否意外捕获 Activity;使用扩展和 apply 时明白它们只改变调用写法,不会管理生命周期。对于共享依赖,能在普通参数和 Context Parameters 之间做有理由的选择。

进入第 2 篇 →

03|类、状态与泛型:让非法状态更难写出来

**讲什么:**以订单页为线索,区分 class、interface、object 与伴随对象;用 data class、enum、sealed、value class 建模页面结果;再说明属性、可见性、继承、in/out、上界和星号投射。Explicit Backing Fields 则展示内部可变、外部只读的属性声明。

**目的:**读完应能决定数据源由谁创建,加载、成功、失败是否需要一个封闭状态层级,以及组件是"生产 T"还是"消费 T"。对外暴露 List 时,也能说清这是实时视图还是快照;不会以为 data class.copy() 会深拷贝,或以为 internal 就是 JVM 安全隔离。

进入第 3 篇 →

04|JVM 与 Android 互操作:源码能编译,旧 AAR 能运行吗

**讲什么:**从 Java 调用方实际看到的签名出发,解释 @JvmStatic、@JvmOverloads、@JvmField;把 @Volatile、@Synchronized、@Throws 分别放回可见性、互斥和异常声明;再看 @Transient、平台类型和接口默认方法的 -jvm-default 模式。

**目的:**读完应能排查"Java 找不到方法""旧模块报 NoSuchMethodError"一类问题,知道何时要检查字节码布局和旧二进制消费者。面对 Java 的未标注空值,先在边界验证;面对敏感字段,不把 @Transient 当成所有序列化框架通用的脱敏开关。

进入第 4 篇 →

05|表达式与控制流:让分支、循环和退出范围可见

讲什么: if 与 when 如何产生值,枚举和密封层级怎样检查分支穷尽;for、while、do-while 各在什么条件下执行;in、闭区间与半开区间、解构声明,以及 return、break、continue 和标签的退出范围。

**目的:**读完应能把页面状态映射写成可检查的结果,遍历列表时避开 size 越界和无上限轮询。看到内联 forEach 中的裸 return,能判断它是否离开外层函数;处理资源或异步任务时,也不会误把"从回调返回"当成"取消请求"。

进入第 5 篇 →

06|集合与序列:从容器选择到数据转换

讲什么: List、Set、Map 的只读与可变接口,Array<T> 与基本类型数组;filter、map、groupBy 对通知列表的转换;Sequence 的惰性求值与 ArrayDeque 的双端操作。文章用"页面要一份稳定列表"和"只取前五条结果"两个需求来判断取舍。

**目的:**读完应能分清只读视图、浅拷贝和真正稳定的数据快照。面对一串集合操作,先确认输出形状和计算时机,再决定是否引入序列;遇到数组或队列 API 时,也会检查复制成本、线程安全和空队列行为。

进入第 6 篇 →

07|异步与并发:从一次搜索请求走到状态流

讲什么: suspend、CoroutineScope、取消的职责;launch、async、Dispatchers 如何组合独立请求;冷 Flow 与持有当前值的 StateFlow 怎样进入 Android 页面;最后比较协程 Mutex 和 JVM 监视器的等待方式。

**目的:**读完应能追踪"用户离开页面,旧请求为何仍回写结果",说明任务归属、取消传播和收集生命周期。遇到重复网络请求,先检查冷流是否被多次收集;更新共享状态时,先列出所有读写入口,再决定是否需要同一把锁。

进入第 7 篇 →

08|工程实践与生态:从测试结果走到可靠发布

**讲什么:**单元测试、Gradle 配置与有条件的性能测量;Compose 状态读取、ViewModel 和生命周期;KMP 中 commonMain 与平台实现的边界;以及公开 API、依赖元数据和旧消费者的发布兼容检查。

**目的:**读完应能为一次 Kotlin 改动挑对验证层次:业务规则用单测,Android 生命周期用设备场景,性能用明确的工作负载,库升级用旧消费者跑关键路径。要做 KMP 时,先共享稳定规则,再评估平台能力;不会把"本地构建通过"等同于"可以发布"。

进入第 8 篇 →

学完之后,用这些面试题检查理解

这些题适合作为自测。回答时尽量说出一条规则、一个失败场景和一个 Android/JVM 落点;能说清边界,比只背定义更有用。

类型、函数与建模

  1. 为什么 Int 不能直接赋给 Long?5 / 2 与 5.toDouble() / 2 各是什么结果?金额计算还需补什么业务约束?
  2. String?、?.、?:、!! 各解决什么问题?Java 平台类型进入 Kotlin 时,你会在哪一层校验?
  3. Any、Unit、Nothing 在函数签名中的含义分别是什么?为什么 error() 能放在 Elvis 运算符右侧?
  4. val list = mutableListOf(...) 是否不可变?val、只读集合接口和不可变快照是同一件事吗?
  5. Lambda 被进程级单例保存时,捕获 Activity 会造成什么问题?尾随 Lambda 与 inline 有什么关系?
  6. 扩展函数按运行时类型还是声明类型解析?扩展属性为什么不能有自己的后备字段?
  7. data class、enum、sealed interface、value class 分别适合哪一种状态?copy() 能否隔离嵌套的可变对象?
  8. 泛型的 out、in、星号投射分别允许哪些安全操作?为什么 MutableList<Photo> 不能直接当作 MutableList<Media>?

控制流、集合与 JVM 边界

  1. when 作为表达式什么时候可以省略 else?给枚举新增成员后,宽泛的 else 会掩盖什么问题?
  2. 0..size、0..<size、list.indices 有什么差别?空列表或倒序遍历时哪个写法更稳妥?
  3. forEach 中的 return 和 return@forEach 各退出哪里?换成非内联回调后还能这样写吗?
  4. List 只读为什么仍可能在 UI 下一帧变化?toList() 做的是哪一层复制?
  5. Array<Int> 与 IntArray 在 JVM 上是什么关系?比较数组内容该用什么方法?
  6. filter、map、groupBy 与 Sequence 的执行时机有何区别?只取前五项时,序列何时可能有收益?
  7. @JvmStatic、@JvmOverloads、@JvmField 分别改变了 Java 看到的什么?删除其中一个注解为什么可能伤到旧调用方?
  8. @Volatile 与 @Synchronized 能否互相替代?@Throws 对 Kotlin 的异常捕获规则有什么影响?
  9. 什么是 Java 平台类型?@Transient 能否保证字段不进入 JSON、Parcelable 或日志?
  10. -jvm-default 改变接口的哪些 JVM 产物?为什么源码重新编译通过,旧 AAR 消费者仍可能运行失败?

协程、Android 与交付

  1. suspend 函数会自动创建新线程吗?launch、async、Dispatchers 分别决定什么?
  2. 新搜索取消旧搜索后,旧结果仍回写界面,你会沿哪几层检查取消是否真正传到底?
  3. 冷 Flow 被两个收集者订阅时会发生什么?StateFlow 适合页面状态,却为什么不保证逐条保存事件?
  4. 协程 Mutex 与 JVM synchronized 在等待时有何差别?持锁后再次获取同一把 Mutex 会怎样?
  5. Compose 重组、页面生命周期与 ViewModel 生命周期各是什么边界?collectAsStateWithLifecycle() 解决哪一层问题?
  6. KMP 的 commonMain 应先共享什么?何时用接口注入,何时用 expect/actual?
  7. 一次库升级通过了单测与本地构建,还应怎样验证性能、公开 API、发布元数据和旧二进制消费者?

如果只能准备一道综合题,可以尝试这样口述:"一个未标注空值的 Java SDK 返回订单列表,Android 页面要筛选、展示并持续更新;用户离开后停止旧请求,库升级后旧 App 仍能运行。你会怎样划分类型、集合、状态、协程和发布验证的边界?" 能从数据进入到旧消费者运行完整走一遍,这八篇就真正连起来了。

相关推荐
kunicoding1 小时前
C++ 策略模式实战:写一个可以“一键换输出“的日志系统
开发语言·c++·策略模式
纪念 2291 小时前
C++算法(三)
开发语言·c++·算法
csdn_zhangchunfeng1 小时前
linux 如何安装rst2man命令
开发语言·chrome·python
sejinan1 小时前
新版!IPTV直播源列表 V1.7 正式发布:接口大厅与会员定制生态解读
开发语言·php
我喜欢就喜欢1 小时前
DeepSeek Harness 开源贡献手记:从零到合入主线的完整历程
开发语言·人工智能·copilot
天空之城--2 小时前
近一周Android开发动态:AI编码、Compose与自适应实践
android
mmsx2 小时前
Android 混淆挡不住二次打包:一条完整的攻击链与突破口
android·混淆
“AI国潮设计-小江”2 小时前
《【AIGC实战】Python+SDXL批量生成“猫咪英歌舞蛋挞”IP,附保持角色一致性的代码逻辑》
开发语言·人工智能·python·prompt·aigc