公司动态

Kotlin 默认可见性为 public,是不是一个好的设计?

📅 2026/8/27 12:35:58
Kotlin 默认可见性为 public,是不是一个好的设计?
前言众所周知Kotlin 的默认可见性为 public而这会带来一定的问题。比如最常见的library 中的代码被无意中声明为 public 的了导致用户使用者可以用到我们不想暴露的 API 这样违背了最小知识原则也不利于我们后续的变更那么既然有这些问题为什么 Kotlin 的默认可见性还被设计成这样呢又该怎么解决这些问题为什么默认为 public其实在 Kotlin M13 版本之前Kotlin 的默认可见性是 internal 的在 M13 版本之后才改成了 public那么为什么会做这个修改呢官方是这样说的In real Java code bases (where public/private decisions are taken explicitly), public occurs a lot more often than private (2.5 to 5 times more often in the code bases that we examined, including Kotlin compiler and IntelliJ IDEA). This means that we’d make people write public all over the place to implement their designs, that would make Kotlin a lot more ceremonial, and we’d lose some of the precious ground won from Java in terms of brevity. In our experience explicit public breaks the flow of many DSLs and very often — of primary constructors. So we decided to use it by default to keep our code clean.总得来说官方认为在实际的生产环境中public 发生的频率要比 private 要高的多比如在 Kotlin 编译器和 InterlliJ 中是 2.5 倍到 5 倍的差距这意味着如果默认的不是 public 的话用户需要到处手动添加 public会增加不少模板代码并且会失去简洁性但是官方这个回答似乎有点问题我们要对比的是 internal 与 public而不是 private 与 public因此也有不少人提出了质疑反方观点包括 JakeWharton 在内的很多人对这一改变了提出了质疑下面我们一起来看下loganj的观点internal 是安全的默认值如果一个类或成员最初具有错误的可见性那么提高可见性要比降低可见性容易得多。也就是说将 internal 类或成员更改为 public 不需要做什么额外的工作因为没有外部调用者在执行相反的操作的成本则很高如果初始时是 public 的你要将它修改为 internal 的就要做很多的兼容工作。因此将 internal 设为默认值可以随着代码库的发展而节省大量工作。分析使用的数据存在缺陷官方提到 public 发生的频率是 private 的 2.5 倍到 5 倍但这是建立在有瑕疵的数据上的由于 Java 提供的可见性选项不足开发人员被迫两害相权取其轻。更有经验的开发人员倾向于通过命名约定和文档来解决这个问题。经验不足的开发人员往往会直接将可见性设置为 public。因此大多数 Java 代码库的 public 类和成员比其作者需要或想要的要多得多。我们不能简单地查看 Java 可见性修饰符在普通代码库中的使用并假设它反映了作者的意愿例如我们常用的 Okhttp 由经验丰富的 Java 开发人员编写的代码库尽管 Java 存在限制但他们仍努力将可见性降至最低。下面是 Okhttp 的 public 包它们旨在构成 Okhttp 的 API这里是它的 internal 包理想情况下只能在模块中被看到。简单计算可以看到大根有 46% 的公共方法和 71% 的公共类。这已经比一般的代码库好很多这是我们应该鼓励的方向。但是 internal 包内部的类根本不应该被公开而这是因为 Java 的可见性限制引起的没有模块内可见如果 Java 有 Kotlin 的可见性修饰符我们应该期望接近 24% 的公共方法和 35% 的 public 类。此外48% 的方法和 65% 的类将是 internal 的internal 的潜力被浪费了在 Java 中别无选择只能通过 public 来实现模块内可见并使用约定和文档来阻止它们的使用。Kotlin 的 internal 可见性修复了 Java 中的这个缺陷但是选择 public 作为默认可见性忽略了这个重要的修正。默认 public 会浪费 Kotlin 内部可见性的潜力。它一反常态地鼓励了 Java 实际上不鼓励的不良做法当 Kotlin 有办法向前迈出一大步时这样做是从 Java 倒退了一大步。正方观点对于一些质疑的观点官方也做了一些回应我们曾经将 internal 设置为默认可见性只是它没有被编译器检查所以它被像 public 一样被使用。然后我们尝试打开检查并意识到我们需要在代码中添加很多 public。在应用(Application)代码而不是库(library)代码中常常包括很多 public。我们分析了很多 case结果发现并不是模块边界布局边界不清晰造成的。模块的划分是完全合乎逻辑的但仍然有很多类由于到处都是 public 关键字而变得非常丑陋。在主构造函数和基于委托属性的 DSL 中这个情况尤其严重每个属性都承受着 public 一遍又一遍地重复的视觉负担因此我们意识到类的成员在默认情况下必须与类本身一样可见。请注意如果一个类是内部的那么它的公共成员实际上也是内部的。所以我们有两个选择默认可见性是公开的 或者类具有与其成员不同的默认可见性。在后一种情况下函数的默认值会根据它是在顶层还是在类中声明而改变。我们决定保持一致因此将默认可见性设置为了 public.对于库作者可以通过 lint 规则和 IDE 检查以确保所有 public 的声明在代码中都是显式的。这会给库代码开发者带来一定的成本但比起不一致的默认可见性或者在应用代码中添加大量 public这似乎并不是一个问题总得来说优点大于缺点。如何解决默认可见性的问题总得来说双方的观点各有各的道理不过从 M13 到现在已经很多年了Kotlin 的可见性一直默认是 public看样子 Kotlin 官方已经下了结论那么我们该如何解决库代码默认可见性为 public导致用户使用者可以用到我们不想暴露的 API 的问题呢Kotlin 官方也提供了一个插件供我们使用binary-compatibility-validator这个插件可以 dump 出所有的 public API将代码与 dump 出来的 api 进行对比可以避免暴露不必要的 api应用插件plugins { id(org.jetbrains.kotlinx.binary-compatibility-validator) version 0.12.1 }应用插件很简单只要在 build.gradle 中添加以上代码就好了插件任务该插件包括两个任务apiDump: 构建项目并将其公共 API 转储到项目 api 子文件夹中。API 以人类可读的格式转储。如果 API 转储文件已经存在它将会被覆盖。apiCheck: 构建项目并检查项目的公共 API 是否与项目 api 子文件夹中的声明相同。如果不同则抛出异常工作流我们可以通过以下工作流确保 library 模块不会无意中暴露 public api准备阶段一次性工作应用插件配置它并执行 apiDump 导出项目 public api手动验证您的公共 API (即执行 apiCheck 任务)。提交项目的 api (即 .api 文件) 到您的 VCS。常规工作流程后续提交代码时都会构建项目并将项目的 API 与 .api 文件声明的 api 进行对比如果两者不同则 check 任务会失败如果是代码问题则将可见性修改为 internal 或者 private再重新提交代码如果的确应该添加新的 public api则通过 apiDump 更新 .api 文件并重新提交与 CI 集成常规工作流程中每次提交代码都应该检查 api 是否发生变化这主要是通过 CI 实现的以 Github Action 为例每次提交代码时都会触发检查如果检查不通过会抛出以下异常总结本文主要介绍了为什么 Kotlin 的默认可见性是 public及其优缺点。同时在这种情况下我们该如何解决 library 代码容易无意中被声明为 public 导致用户使用者可以用到我们不想暴露的 API 的问题如果本文对你有所帮助欢迎点赞~示例项目github.com/RicardoJian…作者程序员江同学链接https://juejin.cn/post/7165659437137395748最后如果想要成为架构师或想突破20~30K薪资范畴那就不要局限在编码业务要会选型、扩展提升编程思维。此外良好的职业规划也很重要学习的习惯很重要但是最重要的还是要能持之以恒任何不能坚持落实的计划都是空谈。如果你没有方向这里给大家分享一套由阿里高级架构师编写的《Android八大模块进阶笔记》帮大家将杂乱、零散、碎片化的知识进行体系化的整理让大家系统而高效地掌握Android开发的各个知识点。相对于我们平时看的碎片化内容这份笔记的知识点更系统化更容易理解和记忆是严格按照知识体系编排的。全套视频资料一、面试合集二、源码解析合集三、开源框架合集欢迎大家一键三连支持若需要文中资料直接点击文末CSDN官方认证微信卡片免费领取↓↓↓