In Kotlin, a function with at least one argument can be defined either as a regular non-member function or as an extension function with one argument being a receiver.

As to the scoping, there seems to be no difference: both can be declared inside or outside classes and other functions, and both can or cannot have visibility modifiers equally.

语言参考似乎不建议在不同的情况下使用常规函数或扩展函数.

所以,我的问题是:when do extension functions give advantage over regular non-member ones?,当常规的超过扩展时?

foo.bar(baz, baq) vs bar(foo, baz, baq).

这仅仅是函数语义的一个暗示(接收者绝对是关注焦点),还是在某些情况下,使用扩展函数会使代码更简洁,或者打开机会?

推荐答案

Extension functions are useful in a few cases, and mandatory in others:

Idiomatic Cases:

  1. 当您想要增强、扩展或更改现有API时.扩展函数是通过添加新功能来更改类的惯用方法.你可以加extension functionsextension properties.请参阅Jackson-Kotlin Module中的一个示例,以向ObjectMapper类添加方法,从而简化TypeReference和泛型的处理.

  2. 将空安全性添加到无法在null上调用的新方法或现有方法.例如,字符串String?.isNullOrBlank()的扩展函数允许您在null字符串上使用该函数,而无需首先执行您自己的null判断.函数本身在调用内部函数之前进行判断.见documentation for extensions with Nullable Receiver

Mandatory Cases:

  1. When you want an inline default function for an interface, you must use an extension function to add it to the interface because you cannot do so within the interface declaration (inlined functions must be final which is not currently allowed within an interface). This is useful when you need inline reified functions, for example this code from Injekt

  2. When you want to add for (item in collection) { ... } support to a class that does not currently support that usage. You can add an iterator() extension method that follows the rules described in the for loops documentation -- even the returned iterator-like object can use extensions to satisfy the rules of providing next() and hasNext().

  3. 向现有类(如+*)添加运算符(专门化#1,但不能以任何其他方式进行,因此是强制性的).见documentation for operator overloading

Optional Cases:

  1. 您希望控制调用方何时可以看到某个对象的范围,因此只能在允许调用可见的上下文中扩展该类.这是可选的,因为您可以随时查看扩展.see answer in other SO question for scoping extension functions

  2. 您有一个界面,希望简化所需的实现,同时仍然允许用户使用更简单的助手功能.您可以 Select 为接口添加默认方法以提供帮助,或者使用扩展函数来添加接口的非预期实现部分.一个允许覆盖默认值,另一个不允许(扩展与成员的优先级除外).

  3. 当您想要将功能与一类功能关联时;扩展函数使用它们的receiver类作为查找它们的位置.它们的名称空间成为可以触发它们的类.而顶级函数将更难找到,并且将填充IDE代码完成对话框中的全局名称空间.您还可以修复现有的库名称空间问题.例如,在Java7中,有Path类,很难找到Files.exist(path)方法,因为它的名字间隔很奇怪.该功能可以直接放在Path.exists()上.(@kirill)

Precedence Rules:

When extending existing classes, keep the precedence rules in mind. They are described in KT-10806 as:

对于当前上下文中的每个隐式接收者,我们try 成员,然后是局部扩展函数(也包括具有扩展函数类型的参数),然后是非局部扩展.

Kotlin相关问答推荐

Kotlin是否针对范围和进度优化sum()?

可选的.在kotlin中不使用泛型参数

如何使用 Kotlin Maven 更改 Minecraft 插件中的 Shulker GUI 标题

generic 类实例列表 - 调用采用 T 的函数

使用 kotlin 流删除 map 中具有某些相似性的值

如何在 kotlin 中使用带有泛型的密封类

顶级属性的初始化

如何将jooq multiset的结果映射到Hashmap(Java Map)?

Kotlin 无法找到或加载主类

jetpack compose 将参数传递给 viewModel

如何从定义它们的类外部调用扩展方法?

在构造函数中仅注入某些参数

如何从 Firestore 查询中排除元素?

Kotlin not nullable值可以为null吗?

从命令行运行Java到Kotlin转换器?

我们如何在Java注释声明中引用Kotlin常量?

Android Kotlin 创建类实现 Parcelable 在 writeToParcel 方法的 override中给出错误

Kotlin数据类打包

有没有办法在Kotlin中设置一个私有常量

函数引用和lambdas