编程后端开发人员

Kotlin中顶级函数和属性的可见性修饰符(internal/private/protected/public)是如何工作的?与Java有什么区别,应该注意哪些细节?

用 Hintsage AI 助手通过面试

回答。

在Kotlin中,可见性修饰符允许控制对声明的访问:类、属性、函数和顶级(文件级)实体。与Java不同,在Java中,修饰符仅在类级别上有效,而在Kotlin中,它们也适用于顶级声明,这对于构建大型项目和库API非常重要。

问题历史

在Java中,函数或属性在类外没有可见性修饰符——一切都在public(或package-private)类内。在Kotlin中,项目的结构通常不同,函数或属性不在类内,而是在文件中。

问题

Java开发人员常常期望public的默认行为与Java中相同,但在Kotlin中,顶级函数(或属性)在所有模块中都是可见的,除非另有标记。不正确的可见性定义可能会导致公共API的词法污染、内部工具的意外可用性,或者所需的公共函数不可用。

解决方案

Kotlin中提供以下修饰符:

  • public:声明在任何地方可见(是顶级的默认修饰符)。
  • internal:声明在同一模块的所有文件中可见(同一gradle模块、同一编译制品、同一jar)。
  • private:仅在声明所在的文件/类中可见。对于顶级,仅在文件内部可见。
  • protected:不适用于顶级声明,仅适用于类/接口及其子类。

示例:

// 文件: Foo.kt private fun utilityFun() {} internal val bar: Int = 10 public val baz: Int = 20 // public不是必需的 fun printValue() { println(bar) }

关键特性:

  • internal限制模块可见性(jar/artifact),而不是包。
  • protected无法用于顶级函数或属性。
  • private在顶级中限制声明在当前文件的范围内。

带陷阱的问题。

可以对顶级函数使用protected吗?

不可以,protected仅与类/接口的成员相关,顶级元素不支持。

如果声明一个internal的顶级函数,它在其他模块内可见吗?

不可以。它仅在当前jar/Gradle模块中可见。

private类与private顶级函数有什么区别?

  • private类:仅在当前文件内可见,无法在文件外使用。
  • private顶级函数或属性:同样仅在文件内可见。

示例:

// 文件: Utils.kt private fun helper() { /* ... */ } // 仅在此文件中可见 internal fun useful() { /* ... */ } // 在整个模块中可见

常见错误和反模式

  • 默认使用public对所有声明进行修饰会导致“杂乱”的自动补全和API。
  • 对于面向外部客户的库,使用internal会隐藏所需的public API。
  • 对protected的混淆和尝试将其应用于顶级。

实际案例

负面案例

测试工具被声明为public,进而进入artefact,干扰了库的客户端——一切不属于public API的都变得可见。

优点:

  • 快速整合。

缺点:

  • 公共API的大小增长,出现“随机”方法可用。

积极案例

内部函数被声明为private,可用于模块内部的公共用途的内部工具,只有经过仔细考虑的接口具有public访问权限。

优点:

  • 清晰、干净的API结构。
  • 随机依赖最小化。

缺点:

  • 需要考虑项目结构。