Effective Kotlin中文版
  • ReadMe
  • 前言
  • 第一部分:良好的代码
    • 第一章:安全性
      • 第1条:限制可变性
      • 第2条:最小化变量的作用域
      • 第3条:尽可能消除平台类型
      • 第4条: 不要暴露需要推断的类型
      • 第5条:指明你期望的参数和状态
      • 第 6 条: 优先使用标准错误,而不是自定错误
      • 第7条:当返回结果可能缺失时,优先使 null 或 Failure
      • 第8条:妥善处理空值
      • 第9条: 使用 use 来关闭资源
      • 第10条:编写单元测试
    • 第二章:可读性
      • 第11条:为了可读性设计代码
      • 第12条:操作符的行为应该与其名称一致
      • 第13条:避免返回或操作 Unit?
      • 第14条: 在变量不清晰时指定其类型
      • 第15条:考虑显式引用接收者
      • 第16:属性应该代表状态,而非行为
      • 第17条:考虑使用具名参数
      • 第18条:遵守编程惯例
  • 第二部分:良好的设计
    • 第三章:可重用性
      • 第19条:不要重复知识
      • 第20条:不要重复实现常用算法
      • 第21条 使用属性代理来提取公共的属性模式
      • 第22条:当实现公共算法时使用泛型
      • 第23条:避免隐藏类型参数
      • 第24条:在使用泛型时考虑型变
      • 第25条:在不同的平台上提取公共模块进行重用
    • 第四章:抽象设计
      • 第26条:每个方法都应该基于单一的抽象级别而编写
      • 第27条:使用抽象来保护代码不受更改
      • 第28条:指定 Api 的稳定性
      • 第29条:考虑包装扩展 API
      • 第30条:最小化元素的可见性
      • 第31条:用文档定义合约
      • 第32条:遵守抽象合约
    • 第五章:对象的创建
      • 第33条:考虑使用工厂方法代替构造函数
      • 第34条:考虑带命名默认参数的主构造函数
      • 第35条:考虑为复杂的对象创建定义 DSL
    • 第六章:类的设计
      • 第36条:组合优于继承
      • 第37条:使用数据修饰符来表示一组数据
      • 第38条:使用函数类型而不是接口来传递操作和行为
      • 第39条:类层次结构优于标签类
      • 第40条:遵守 equals 的合约
      • 第41条:遵守 hashCode 的合约
      • 第42条:遵守 compareTo 的合约
      • 第43条: 考虑将 API 的非必要部分提取到扩展函数中
      • 第44条:避免在成员中定义扩展
  • 第三部分:性能
    • 第七章:让开发成本更低
      • 第45条:避免不必要的对象创建
      • 第46条:给高阶函数使用 inline 修饰符
      • 第47条:考虑使用内联类
      • 第48条:消除过时的对象引用
    • 第八章:高效的集合处理
      • 第49条:在具有多个处理步骤的大型集合上,优先使用 Sequence
      • 第50条:限制操作步骤的数量
      • 第51条:性能关键处考虑使用原语的数组
      • 第52条:考虑使用可变集合
Powered by GitBook
On this page
  • 合约是继承的
  • 总结
  1. 第二部分:良好的设计
  2. 第四章:抽象设计

第32条:遵守抽象合约

合约和可见性都是开发者之间的一种协议。用户几乎总是可能违反此协议。从技术上来讲,一个项目中的任何地方都可能被骇掉。例如,可以使用反射来打开和使用我们想要的任何东西:

class Employee {
    private val id: Int = 2
    
    override fun toString() = "User(id=$id)"
    
    private fun privateFunction() {
        println("Private function called")
    }
}

fun callPrivateFunction(employee: Employee) {
    employee::class.declaredMemberFunctions
        .first { it.name == "privateFunction" }
        .apply { isAccessible = true }
        .call(employee)
}

fun changeEmployeeId(employee: Employee, newId: Int) {
    employee::class.java.getDeclaredField("id")
        .apply { isAccessible = true }
        .set(employee, newId)
}

fun main() {
    val employee = Employee()
    callPrivateFunction(employee) // Prints: Private function called
    changeEmployeeId(employee, 1)
    print(employee) // Prints: User(id=1)
}

仅仅因为你能做某件事情,并不意味着做这件事情就是好的。在这个例子中,我们非常依赖于实现细节,比如私有属性和私有函数的名称,它们根本不是合约的一部分,因此我们随时都可能发生变化,这种代码对我们的项目来说就像是一个定时炸弹。

记住,合约就像防火墙一样,只要你正确的使用你的电脑,防火墙就会保护你。 当你打开电脑并开始骇入它时,就同时失去了防火墙的保护。 同样的原则也适用于此:当你违反合约时,实现的更改导致代码停止了工作,就是你的问题。

合约是继承的

当我们从类继承或者从另一个库扩展接口时,遵守合约特别重要。记住,你的目标实现应该遵守它们的合约,例如,每个类都继承 Any 的 equals 和 hashCode 方法,我们都要遵守其完善的合约,否则对象就无法正常工作。比如,当 hashCode 和 equals 不一致时,对象在 HashSet 的行为就可能不正确,下面行为是不正确的,因为一个集合不应该允许重复:

class Id(val id: Int) {
    override fun equals(other: Any?) = other is Id && other.id == id
}

val mutableSet = mutableSetOf(Id(1))
mutableSet.add(Id(1))
mutableSet.add(Id(1))
print(mutableSet.size) // 3

在本例中,是 hashCode 的实现与 equals 不一致,我们将在_第6章:类的设计_中讨论一些重要的 Kotlin 合约,现在,请记住检查你的重写的函数的期望,并遵守这些期望。

总结

如果你希望你的程序是稳定的,遵守合约,当你不得不打破某些规则时,那么就请好好记录下这个事实,这些信息对维护你代码的人员非常有用。也许在几年后,你也会成为那样的人。

Previous第31条:用文档定义合约Next第五章:对象的创建

Last updated 2 years ago