-
「Generated by Manus, instructions issued by binbinwang」
1.1 代码审查清单(前25项) 代码审查是确保代码质量的重要环节。对于从Objective-C迁移到Kotlin的iOS开发者来说,了解Kotlin特有的代码审查要点尤为重要。本章将提供50个Kotlin代码审查的高频注意事项,帮助你编写高质量的Kotlin代码。
1. 使用数据类表示模型 说明 :对于主要用于保存数据的类,应使用数据类(data class)。
可能的后果 :不使用数据类会导致需要手动实现equals()
、hashCode()
、toString()
等方法,增加代码量并可能引入错误。
不推荐 :
1 class User(val id: Int, val name: String, val email: String)
推荐 :
1 data class User(val id: Int, val name: String, val email: String)
2. 优先使用不可变数据 说明 :尽可能使用val
而非var
声明变量,使用不可变集合。
可能的后果 :可变数据增加了代码复杂性,可能导致并发问题和意外的副作用。
不推荐 :
1 2 3 var user = User(1, "John", "john@example.com") val mutableList = mutableListOf(1, 2, 3) mutableList.add(4)
推荐 :
1 2 val user = User(1, "John", "john@example.com") val list = listOf(1, 2, 3, 4)
3. 合理使用扩展函数 说明 :使用扩展函数为现有类添加功能,而不是创建工具类。
可能的后果 :不使用扩展函数会导致代码组织不良,降低可读性和可维护性。
不推荐 :
1 2 3 4 5 6 7 8 9 // 工具类 object StringUtils { fun isEmailValid(email: String): Boolean { return android.util.Patterns.EMAIL_ADDRESS.matcher(email).matches() } } // 使用 if (StringUtils.isEmailValid(email)) { /* ... */ }
推荐 :
1 2 3 4 5 6 7 // 扩展函数 fun String.isEmailValid(): Boolean { return android.util.Patterns.EMAIL_ADDRESS.matcher(this).matches() } // 使用 if (email.isEmailValid()) { /* ... */ }
4. 利用密封类表示有限状态 说明 :对于有限的类层次结构,使用密封类(sealed class)。
可能的后果 :不使用密封类会导致when
表达式需要else
分支,增加出错风险。
不推荐 :
1 2 3 4 5 6 7 8 9 10 11 12 interface Result { class Success(val data: String) : Result class Error(val exception: Exception) : Result } fun handleResult(result: Result) { when (result) { is Result.Success -> { /* 处理成功 */ } is Result.Error -> { /* 处理错误 */ } else -> { /* 可能遗漏新增的子类 */ } } }
推荐 :
1 2 3 4 5 6 7 8 9 10 11 12 sealed class Result { data class Success(val data: String) : Result() data class Error(val exception: Exception) : Result() } fun handleResult(result: Result) { when (result) { is Result.Success -> { /* 处理成功 */ } is Result.Error -> { /* 处理错误 */ } // 不需要else分支,编译器会检查是否穷尽所有可能 } }
5. 使用高阶函数和Lambda表达式 说明 :利用Kotlin的函数式编程特性,使用高阶函数和Lambda表达式简化代码。
可能的后果 :不使用函数式特性会导致代码冗长,难以维护。
不推荐 :
1 2 3 4 5 6 7 // 命令式风格 val evenNumbers = ArrayList<Int>() for (number in numbers) { if (number % 2 == 0) { evenNumbers.add(number) } }
推荐 :
1 2 // 函数式风格 val evenNumbers = numbers.filter { it % 2 == 0 }
6. 使用内联函数优化性能 说明 :对于包含Lambda参数的小函数,考虑使用inline
关键字以减少运行时开销。
可能的后果 :不使用内联函数可能导致创建额外的函数对象,增加内存开销和方法调用开销。
不推荐 :
1 2 3 4 5 6 7 fun <T> measureTime(block: () -> T): T { val start = System.nanoTime() val result = block() val end = System.nanoTime() println("Time taken: ${end - start} ns") return result }
推荐 :
1 2 3 4 5 6 7 inline fun <T> measureTime(block: () -> T): T { val start = System.nanoTime() val result = block() val end = System.nanoTime() println("Time taken: ${end - start} ns") return result }
7. 正确使用协程进行异步编程 说明 :使用协程处理异步任务,避免回调地狱。
可能的后果 :不正确使用协程可能导致内存泄漏、线程阻塞或异常未处理。
不推荐 :
1 2 3 4 5 6 7 8 9 10 11 // 使用回调 fun fetchUserData(userId: String, callback: (User?, Error?) -> Unit) { thread { try { val user = api.getUser(userId) callback(user, null) } catch (e: Exception) { callback(null, e) } } }
推荐 :
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 // 使用协程 suspend fun fetchUserData(userId: String): User { return withContext(Dispatchers.IO) { api.getUser(userId) } } // 使用 lifecycleScope.launch { try { val user = fetchUserData("123") updateUI(user) } catch (e: Exception) { handleError(e) } }
8. 使用Flow处理响应式数据流 说明 :对于需要随时间变化的数据流,使用Flow而非回调或LiveData。
可能的后果 :不使用Flow处理数据流可能导致代码复杂、难以测试和维护。
不推荐 :
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 // 使用回调 interface UserUpdatesListener { fun onUserUpdated(user: User) } class UserRepository { private val listeners = mutableListOf<UserUpdatesListener>() fun addListener(listener: UserUpdatesListener) { listeners.add(listener) } fun removeListener(listener: UserUpdatesListener) { listeners.remove(listener) } fun updateUser(user: User) { listeners.forEach { it.onUserUpdated(user) } } }
推荐 :
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 // 使用Flow class UserRepository { private val _userFlow = MutableStateFlow<User?>(null) val userFlow: StateFlow<User?> = _userFlow.asStateFlow() fun updateUser(user: User) { _userFlow.value = user } } // 使用 lifecycleScope.launch { userRepository.userFlow .filterNotNull() .collect { user -> updateUI(user) } }
9. 避免空指针异常 说明 :充分利用Kotlin的空安全系统,避免空指针异常。
可能的后果 :不正确处理空值可能导致运行时崩溃。
不推荐 :
1 2 3 4 5 6 7 // 使用非空断言(可能导致NPE) val length = user!!.name.length // 忽略可能的空值 fun getNameLength(user: User?): Int { return user!!.name.length // 危险! }
推荐 :
1 2 3 4 5 6 7 // 使用安全调用 val length = user?.name?.length ?: 0 // 明确处理空值 fun getNameLength(user: User?): Int { return user?.name?.length ?: 0 }
10. 使用函数式集合操作 说明 :优先使用函数式集合操作(如map、filter、reduce)而非命令式循环。
可能的后果 :命令式循环代码冗长,更容易出错,可读性较差。
不推荐 :
1 2 3 4 5 6 7 // 命令式循环 val result = mutableListOf<String>() for (number in numbers) { if (number % 2 == 0) { result.add("Even: $number") } }
推荐 :
1 2 3 4 // 函数式操作 val result = numbers .filter { it % 2 == 0 } .map { "Even: $it" }
11. 避免不必要的对象创建 说明 :避免在循环或频繁调用的方法中创建临时对象。
可能的后果 :过多的对象创建会增加垃圾回收压力,影响性能。
不推荐 :
1 2 3 4 5 6 // 在循环中创建临时对象 for (i in 1..1000) { val formatter = SimpleDateFormat("yyyy-MM-dd") val dateStr = formatter.format(Date()) println(dateStr) }
推荐 :
1 2 3 4 5 6 // 重用对象 val formatter = SimpleDateFormat("yyyy-MM-dd") for (i in 1..1000) { val dateStr = formatter.format(Date()) println(dateStr) }
12. 使用const val声明编译时常量 说明 :对于编译时已知的常量,使用const val
而非val
。
可能的后果 :不使用const val
会导致运行时而非编译时计算常量,影响性能。
不推荐 :
1 2 3 // 运行时常量 val PI = 3.14159 val MAX_COUNT = 100
推荐 :
1 2 3 // 编译时常量 const val PI = 3.14159 const val MAX_COUNT = 100
13. 优化循环性能 说明 :对于大型集合的处理,考虑使用序列(Sequence)避免中间集合创建。
可能的后果 :不使用序列可能导致创建多个中间集合,增加内存开销。
不推荐 :
1 2 3 4 5 // 创建多个中间集合 val result = list .map { it * 2 } .filter { it > 10 } .take(5)
推荐 :
1 2 3 4 5 6 // 使用序列避免中间集合 val result = list.asSequence() .map { it * 2 } .filter { it > 10 } .take(5) .toList()
14. 使用命名参数提高可读性 说明 :对于多参数函数,使用命名参数提高代码可读性。
可能的后果 :不使用命名参数可能导致参数用途不明确,增加错误风险。
不推荐 :
1 2 // 参数用途不明确 setUserPreferences(true, false, true, 10)
推荐 :
1 2 3 4 5 6 7 // 命名参数清晰表明用途 setUserPreferences( receiveEmails = true, receiveNotifications = false, useDarkTheme = true, fontSize = 10 )
15. 使用作用域函数简化代码 说明 :合理使用作用域函数(let、apply、run、also、with)简化代码。
可能的后果 :不使用作用域函数可能导致代码冗长,变量作用域过大。
不推荐 :
1 2 3 4 5 6 // 冗长的代码 val user = User("John", "Doe") user.firstName = "Jane" user.lastName = "Smith" println(user) return user
推荐 :
1 2 3 4 5 6 7 // 使用apply简化 return User("John", "Doe").apply { firstName = "Jane" lastName = "Smith" }.also { println(it) }
16. 避免过度使用单例 说明 :谨慎使用单例(object)和伴生对象(companion object),避免全局状态。
可能的后果 :过度使用单例会导致全局状态难以测试和维护,可能引入并发问题。
不推荐 :
1 2 3 4 5 6 7 8 9 10 11 12 13 // 过度使用单例 object UserManager { var currentUser: User? = null fun login(username: String, password: String) { // 实现登录逻辑 currentUser = User(username, "email@example.com") } fun logout() { currentUser = null } }
推荐 :
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 // 依赖注入 class UserManager @Inject constructor(private val api: UserApi) { private val _currentUser = MutableStateFlow<User?>(null) val currentUser: StateFlow<User?> = _currentUser.asStateFlow() suspend fun login(username: String, password: String): Result<User> { return try { val user = api.login(username, password) _currentUser.value = user Result.success(user) } catch (e: Exception) { Result.failure(e) } } fun logout() { _currentUser.value = null } }
17. 正确处理协程异常 说明 :在协程中正确处理异常,避免异常被吞噬。
可能的后果 :不正确处理协程异常可能导致错误被忽略,难以调试。
不推荐 :
1 2 3 4 5 6 7 8 9 // 异常被吞噬 viewModelScope.launch { try { repository.fetchData() } catch (e: Exception) { // 记录错误但不通知UI Log.e("ViewModel", "Error fetching data", e) } }
推荐 :
1 2 3 4 5 6 7 8 9 10 11 12 13 // 正确处理异常 private val _error = MutableLiveData<String>() val error: LiveData<String> = _error viewModelScope.launch { try { val result = repository.fetchData() processResult(result) } catch (e: Exception) { Log.e("ViewModel", "Error fetching data", e) _error.value = e.message ?: "Unknown error" } }
18. 使用委托属性 说明 :利用Kotlin的委托属性简化常见模式。
可能的后果 :不使用委托属性可能导致重复代码和样板代码。
不推荐 :
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 // 手动实现延迟初始化 class ResourceManager { private var _resources: Resources? = null val resources: Resources get() { if (_resources == null) { _resources = loadResources() } return _resources!! } private fun loadResources(): Resources { // 加载资源 return Resources() } }
推荐 :
1 2 3 4 5 6 7 8 9 10 11 // 使用lazy委托 class ResourceManager { val resources: Resources by lazy { loadResources() } private fun loadResources(): Resources { // 加载资源 return Resources() } }
19. 避免使用原始类型数组 说明 :优先使用集合类型(List、Set、Map)而非原始类型数组。
可能的后果 :使用原始类型数组缺少集合API的便利方法,可能导致代码冗长。
不推荐 :
1 2 3 4 5 6 // 使用原始类型数组 val numbers = IntArray(5) { it * 2 } var sum = 0 for (number in numbers) { sum += number }
推荐 :
1 2 3 // 使用集合类型 val numbers = List(5) { it * 2 } val sum = numbers.sum()
20. 使用解构声明 说明 :利用解构声明简化代码。
可能的后果 :不使用解构声明可能导致代码冗长,可读性较差。
不推荐 :
1 2 3 4 5 6 7 8 9 10 11 // 不使用解构 val pair = Pair("John", 30) val name = pair.first val age = pair.second // 遍历Map for (entry in map) { val key = entry.key val value = entry.value println("$key: $value") }
推荐 :
1 2 3 4 5 6 7 // 使用解构 val (name, age) = Pair("John", 30) // 遍历Map for ((key, value) in map) { println("$key: $value") }
21. 使用类型别名提高可读性 说明 :对于复杂类型,使用类型别名(typealias)提高代码可读性。
可能的后果 :不使用类型别名可能导致复杂类型难以理解和维护。
不推荐 :
1 2 3 4 // 复杂类型难以理解 fun processData(data: Map<String, List<Pair<Int, String>>>) { // 处理数据 }
推荐 :
1 2 3 4 5 6 7 8 9 10 // 使用类型别名 typealias UserId = Int typealias UserName = String typealias UserInfo = Pair<UserId, UserName> typealias UsersList = List<UserInfo> typealias UsersMap = Map<String, UsersList> fun processData(data: UsersMap) { // 处理数据 }
22. 避免过度嵌套函数 说明 :避免在函数内定义多层嵌套函数,考虑提取为顶级函数或扩展函数。
可能的后果 :过度嵌套函数会降低代码可读性和可维护性。
不推荐 :
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 // 过度嵌套函数 fun processUserData(users: List<User>) { fun filterActiveUsers(users: List<User>): List<User> { return users.filter { it.isActive } } fun mapToUserInfo(users: List<User>): List<UserInfo> { fun createUserInfo(user: User): UserInfo { return UserInfo(user.id, user.name) } return users.map { createUserInfo(it) } } val activeUsers = filterActiveUsers(users) val userInfos = mapToUserInfo(activeUsers) // 处理userInfos }
推荐 :
1 2 3 4 5 6 7 8 9 10 11 12 13 14 // 提取为顶级函数或扩展函数 private fun List<User>.filterActive(): List<User> { return filter { it.isActive } } private fun User.toUserInfo(): UserInfo { return UserInfo(id, name) } fun processUserData(users: List<User>) { val activeUsers = users.filterActive() val userInfos = activeUsers.map { it.toUserInfo() } // 处理userInfos }
23. 使用枚举类表示固定选项 说明 :对于固定选项集,使用枚举类(enum class)而非常量。
可能的后果 :使用常量表示选项可能导致类型安全问题和代码难以维护。
不推荐 :
1 2 3 4 5 6 7 8 9 10 11 12 13 // 使用常量 const val STATUS_PENDING = 0 const val STATUS_ACTIVE = 1 const val STATUS_COMPLETED = 2 fun processStatus(status: Int) { when (status) { STATUS_PENDING -> println("Pending") STATUS_ACTIVE -> println("Active") STATUS_COMPLETED -> println("Completed") else -> println("Unknown") // 可能接收无效值 } }
推荐 :
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 // 使用枚举类 enum class Status { PENDING, ACTIVE, COMPLETED } fun processStatus(status: Status) { when (status) { Status.PENDING -> println("Pending") Status.ACTIVE -> println("Active") Status.COMPLETED -> println("Completed") // 不需要else分支,编译器确保穷尽所有可能 } }
24. 避免硬编码字符串 说明 :避免在代码中硬编码字符串,使用常量或资源文件。
可能的后果 :硬编码字符串难以维护,不利于本地化和统一修改。
不推荐 :
1 2 3 4 // 硬编码字符串 fun showError() { Toast.makeText(context, "An error occurred. Please try again.", Toast.LENGTH_SHORT).show() }
推荐 :
1 2 3 4 5 6 7 // 使用资源文件 fun showError() { Toast.makeText(context, R.string.error_message, Toast.LENGTH_SHORT).show() } // strings.xml <string name="error_message">An error occurred. Please try again.</string>
25. 使用适当的可见性修饰符 说明 :使用最严格的可见性修饰符(private、protected、internal、public)限制API暴露。
可能的后果 :过度暴露API会增加维护难度,破坏封装性。
不推荐 :
1 2 3 4 5 6 7 8 9 10 11 12 // 过度暴露API class UserRepository(val api: UserApi) { var cachedUsers: List<User> = emptyList() fun fetchUsers() { // 实现 } fun clearCache() { cachedUsers = emptyList() } }
推荐 :
1 2 3 4 5 6 7 8 9 10 11 12 13 // 适当限制可见性 class UserRepository(private val api: UserApi) { private var _cachedUsers: List<User> = emptyList() val cachedUsers: List<User> get() = _cachedUsers fun fetchUsers() { // 实现 } internal fun clearCache() { _cachedUsers = emptyList() } }